首页 文章 精选 留言 我的

精选列表

搜索[快速],共10000篇文章
优秀的个人博客,低调大师

Microsoft Edge 浏览器将支持快速启动和标签睡眠

在今天的 Microsoft 年度 Build 大会上,微软宣布了其 Edge 浏览器即将发布的 91 版本的一些新功能。这些功能都不是大改动,但对其用户来说可以充分提高使用体验。 一个新功能是 "启动加速",它允许 Edge 几乎立即启动。Microsoft 实现该功能的方式很直接。当用户启动Windows 设备时,会简单地加载一些核心的 Edge 进程,所以当 Edge 真正启动时可以迅速完成。而且据 Microsoft 表示,这并不会对用户的 Windows 10 启动时间有太大的影响。 另一个新功能是 "睡眠标签",它可以让用户的 edge 标签页进入睡眠状态,这样它们就不会占用不必要的内存和 CPU 周期。Microsoft 在12月首次宣布它正在测试这项功能。据当时的 Edge 开发者表示,睡眠标签与不睡眠的标签相比,平均仅使用 37% 的CPU,且可以减少 32% 的内存使用,并有助于改善电池寿命。值得注意的是,Google 的 Chrome 浏览器,与 Edge 共享大部分底层技术,也具有限制资源使用的工具,包括所谓的 "标签冻结"。

优秀的个人博客,低调大师

vn.py快速入门7 - 历史数据回测优化

策略已经写好了,下一步就是历史回测:把历史上的价格数据(K线或者Tick),推送给策略去运行交易逻辑,并把策略产生的交易记录下来,最后分析这些回测的交易记录,从而来判断该策略的潜在盈利能力。 在开始之前,先来讲几个量化策略研究中(不管是否用vn.py),需要记住的几条重要原则: 所有量化程序的回测功能,永远都只能尽量接近实盘交易中的各项细节,而无法做到100%一样,关键点在于误差的大小(是否能容忍); 回测效果好的策略,并不能代表实盘交易就一定盈利,可能存在交易成本误差、参数过度拟合、逻辑有未来函数或者市场特征变化(Regime Switch)等原因; 回测效果烂的策略,实盘交易基本可以保证会更烂,绝对不要有侥幸心理。 准备历史数据 要跑历史数据回测,第一步自然就是要先准备好历史数据。这里我们以国内期货数据为例,使用米筐的RQData来下载获取。 RQData目前提供30天的免费试用权限,网站申请非常方便。前往RQData主页 找到上图中的“免费试用”按钮,点击进去后: 根据自己的实际情况填写相应的注册信息,邀请码点击那个白色小问号图标,可以看到没有邀请码情况下的默认输入值(当前是ClOR,注意l是小写的L,而不是大写的i),点击“登录并申请”按钮后,会看到登录框: 选择“验证码登录”,输入手机号验证码后点击“确认登录”按钮,回到上一步的界面,但注意此时底部按钮显示的文字已变为“立即申请试用”: 点击上述按钮后完成试用申请,注意此时有可能出现验证码超时或者其他的错误信息,根据提示重新填写再点击按钮即可。申请成功后会自动弹出开始下载[make.bat]文件(该文件中即包含了申请的试用账号和密码),以及[RQDATA使用说明.pdf]。 使用VS Code打开make.bat文件: 记住其中的name(用户名)以及password(密码),注意密码是一串长达344个字符的密钥,上图中仅截取了很短一部分(别想偷懒,哈哈)。 然后运行VN Station,点击VN Trader Pro,在右侧的上层应用中加载CtaBacktester(CTA回测模块)后启动,在主界面顶部的菜单栏,找到“配置”按钮: 点击后打开VN Trader的全局配置对话框: 将之前已经准备好的RQData用户名和密码,分别填入到rqdata.username和rqdata.password两个字段中,然后点击“确定”按钮,弹出提示重启的对话框。 此时即可关闭VN Trader并重启,点击菜单栏“功能”->“CTA回测”,启动接下来我们要用到的CTA策略回测图形界面: 如果上一步的RQData账号密码配置正确,此时可以在中间底部的日志输出框中看到“RQData数据接口初始化成功”的信息。如果没有就说明配置有问题,回去重来吧。 窗口左上方的一系列编辑框和下拉框,用来控制和管理我们的回测功能。在本地代码编辑框中输入IF88.CFFEX,K线周期选择1m(即1分钟K线),然后选择要下载数据的开始日期和结束日期,点击“下载数据”按钮。 此时CtaBacktester模块就会自动从RQData服务器下载历史数据,并完成数据结构转化后插入到VN Trader的数据库中(默认使用SQLite,数据文件位于.vntrader目录下的database.db),下载完成后同样会在日志输出框中看到相应信息: 运行历史回测 有了历史数据后,我们就可以开始跑历史回测。在左上角的交易策略下拉框里,应该已经能找到上一篇教程我们编写的DemoStrategy,选中后开始配置回测参数。 注意这里我们使用的是中金所股指期货的IF合约,回测时的参数要设置为股指期货所对应的属性。手续费率编辑框中输入0.000025(万0.25),交易滑点输入0.2(即单边成交1跳的滑点成本),合约乘数为300(300元每点),价格跳动也是0.2(股指期货最小价格变动),回测资金我们使用100万。 点击“开始回测”按钮,弹出参数配置对话框: 这里显示的fast_window和slow_window就是之前我们添加到parameters列表中的参数名称,这里我们直接使用默认数值,点击“确定”按钮后,我们的回测引擎就会自动开始执行策略回测的整个流程:加载数据、数据回放、模拟撮合、计算每日盈亏、统计指标、以及最后画出图表: 不出之前的意料,双均线策略的效果差的一塌糊涂,右侧图表的4个子图中: 子图1:资金变化曲线,笔直向下说明稳定亏损 子图2:最大回撤曲线,越来越大说明策略亏损越来越多 子图3:每日盈亏统计,红绿分布平均,但绿色密度更大(亏损) 子图4:盈亏的概率分布图,尖峰在0轴左侧(中位数日期发生亏损) 然后在中间顶部的表格中,可以看到回测相关的一些统计数据: 这策略干了什么事情能亏这么多钱呢,总有很多人会抱着不信邪的态度,此时点击左侧的“成交记录”按钮,可以看到回测过程中的每一笔成交记录: 还不信邪,可以点击“委托记录”按钮查看这些成交具体是由哪些委托触发的: 可以通过“成交记录”中的每条成交对应的委托号,在“委托记录”中找到策略下出的委托。细心的人可能已经发现上面两张图中,某一笔委托的价格和其对应成交的价格并不一致,这是因为我们在策略下单时使用了超价5元(为了保证成交),而回测仿真撮合时则是取了T+1时刻的最优成交价(也是实盘中最可能拿到的价格)。 在“每日盈亏”窗口中,可以看到以逐日盯市规则(期货结算规则),将每日的持仓和成交映射到当日收盘价后的当日整体盈亏情况: 最后,如果还是觉得死活不相信双均线策略怎么可能这么差,点击“K线图表按钮”,可以看到整个回测数据对应的K线图表: 上图中的黄色向上箭头代表多头成交(buy/cover),蓝色向下箭头则代表了空头成交(sell/short),可以通过键盘和鼠标拖动和缩放图表,看到自己想要的部分。 到了这里,是不是已经有点相信了“双均线策略就是垃圾”的说法?实际上从公平角度讲,以上看到的回测信息,并不能充分证明双均线信号的无效性,如果我们: 将手续费和滑点都调为0,这样不考虑交易成本影响 然后在回测时缩小fast_window到3,增大slow_window到80,即让长周期均线变得更加平稳 出来的结果则变成了: 从这张图上看,在不考虑交易成本时,双均线的来回穿插作为一种信号,可能还是有一定的预测效果(尽管也好不到哪里去)。 策略参数优化 实盘用可能是没希望了,但不妨碍我们想来折腾一下,看看在股指1分钟数据上到底怎样的均线组合能起到最好的效果,毕竟之前的3和80两个参数纯粹只是拍脑袋的结果。 假设我们想要看看fast_window,从2到20(步进2),slow_window,从20到100(步进10),参数分别两两组合出来的回测效果,看看能不能找到更好的均线组合。 比较傻的方法就是人工操作,每次将两个参数输入到回测的参数对话框里,然后运行等结果,再把结果记录在Excel表格里最后用来做排序比较。但对于这种机械重复的劳动,电脑比起人的效率要高得多得多,在本质上我们就是分别遍历两个参数的各种可能排列组合,然后针对每组组合,跑完回测并记录其中的关键结果,也就是所谓的“参数优化”。 CtaBacktester模块已经内置了策略参数优化的功能,点击左侧下方的“参数优化”按钮: 在弹出的对话框中,我们把之前的参数想法输入进去: 目标函数就选择最简单的总收益率 fast_window,开始数值为2,结束数值为20,每次步进为2(即2、4、6、8、10...) slow_window,开始数值为20,结束数值为100,每次步进为10(即20、30、40、50、60...) 点击“多进程优化”,使用暴力穷举算法(Brute-Force Algorithm),同时运行多个并行的Python进程,充分利用CPU的核心数量来加快优化速度。 优化完成后,日志信息中会有相应的提示,同时左下角的“优化结果”按钮会亮起: 点击后看到每组参数组合,所对应的目标函数结果: 效果最好的是fast_window为18,slow_window为90,带入到策略回测中运行后: 参数优化大法好!!! 可能在前面铺垫了那么多的情况下(过度拟合风险、双均线信号普通、移除了滑点手续费),你不见得还会脑子里蹦出这么一句话,但不可否认参数优化后的效果提升非常明显。 在本篇教程的最后,希望提醒大家的是:尽管看起来一路点点鼠标就能搞出个漂亮的资金曲线了,但实际上量化策略回测和优化过程中充满了各种各样的地雷。 到目前为止我们所讲述的只是最最基础的操作方法,还远没有涉及到实践经验的内容,这块要么大家用自己的真金白银在交易中慢慢积累,另一个成本更低的选择当然就是关注我们后续的进阶教程了! 了解更多知识,请关注vn.py社区公众号。

优秀的个人博客,低调大师

快速了解Service Mesh微服务架构实现服务间gRPC通信

在前面的文章之中我们介绍了基于Kubernetes及Istio如何一步一步把Service Mesh微服务架构玩起来!在该文章中,我们演示了一个非常贴近实战的案例,这里回顾下该案例的结构,如下图所示: 该案例所演示的就是我们日常使用微服务架构开发时,服务间最普遍的通信场景。在Spring Cloud微服务体系中,服务间可以通过Fegin+Ribbon组合的方式,实现服务间负载均衡方式的Http接口调用;但在Service Mesh架构中,服务发现及负载均衡等治理逻辑已经由SideCar代理,如果还希望延续Spring Cloud场景下服务间接口调用的代码体验,一般可以通过改写Feign组件,去掉其中关于服务治理的逻辑,只保留简单的接口声明式调用逻辑来实现。 上述案例中“micro-api->micro-order”之间的服务通信调用,就是基于该方式实现的(可参考之前的文章)。但在微服务架构中除了采用Http协议通信外,对于某些对性能有着更高要求的系统来说,采用通信效率更高的RPC协议往往是更合适的选择! 在基于Spring Cloud框架的微服务体系中,服务之间也可以通过RPC协议通信,但由于服务治理的需要,也需要一套类似于Fegin+Ribbon组合的SDK支持。例如gRPC框架就有针对Spring Boot框架的“grpc-client-spring-boot-starter”依赖支持!该项目是一个 gRPC 的 Spring Boot 模块,可以在 Spring Boot 中内嵌一个 gRPC Server 对外提供服务,并支持 Spring Cloud 的服务发现、注册、链路跟踪等等。 那么在Service Mesh微服务体系下,服务间基于gRPC框架的通信应该怎么实现呢?接下来,我将以案例中"micro-order->micro-pay"之间的服务调用为例,演示在Service Mesh微服务架构下实现服务间的gRPC通信调用,并将案例中Http+gRPC服务间通信的完整场景串起来! gRPC概述 在演示Service Mesh微服务架构下的gRPC通信场景之前,我们先简单介绍下RPC协议及gRPC框架的基本知识。 RPC(Remote Procedure Call),又称远程过程调用,是一种通过掩藏底层网络通信复杂性,从而屏蔽远程和本地调用区别的通信方式。相比于Http协议,RPC协议属于一种自定义的TCP协议,从而在实现时避免了一些Http协议信息的臃肿问题,实现了更高效率的通信。 在主流实现RPC协议的框架中,比较著名的有Dubbo、Thrift及gRPC等。因为目前主流的容器发布平台Kubernetes,以及Service Mesh开源平台Istio都是通过gRPC协议来实现内部组件之间的交互,所以在Service Mesh微服务架构中,服务间通信采用gRPC协议,从某种角度上说会更具有原生优势。况且在此之前,gRPC框架已经在分布式、多语言服务场景中得到了大量应用,因此可以预测在Service Mesh微服务架构场景下,基于gRPC框架的微服务通信方式会逐步成为主流。 gRPC是Google发布的基于HTTP/2.0传输层协议承载的高性能开源软件框架,提供了支持多种编程语言的、对网络设备进行配置和纳管的方法。由于是开源框架,通信的双方可以进行二次开发,所以客户端和服务器端之间的通信会更加专注于业务层面的内容,减少了对由gRPC框架实现的底层通信的关注。 接下来的内容就具体演示在Service Mesh微服务架构下,实现微服务“micro-order->micro-pay”的gRPC通信调用! 构建gRPC服务端程序(micro-pay) 首先从gRPC服务端的角度,在微服务micro-pay项目中集成gRPC-Java,并实现一个gRPC服务端程序。具体如下: 1、构建Spring Boot基本工程(micro-pay/micro-pay-client) 使用Spring Boot框架构建基本的Maven工程,为了工程代码的复用,这里单独抽象一个micro-pay-client工程,并定义micro-pay微服务gRPC服务接口的protobuf文件(*/proto/paycore.proto),代码如下: syntax = "proto3"; package com.wudimanong.pay.client; option java_multiple_files = true; option java_package = "com.wudimanong.micro.pay.proto"; service PayService { //定义支付rpc方法 rpc doPay (PayRequest) returns (PayResponse); } message PayRequest { string orderId = 1; int32 amount=2; } message PayResponse { int32 status = 1; } 如上所示,创建了一个基于protobuf协议的支付接口定义文件,其中定义了支付服务PayService及其中的doPay支付rpc方法,并定义了其请求和返回参数对象,具体的语法遵循“proto3”协议。 为了能够正常编译和生成protobuf文件所定义服务接口的代码,需要在项目pom.xml文件中引入jar包依赖及Maven编译插件配置,代码如下: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> .... <dependencies> .... <!--gRPC通信类库(截止目前的最新版本)--> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-all</artifactId> <version>1.36.1</version> </dependency> </dependencies> <build> <!--引入gRpc框架proto文件编译生产插件--> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.2</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.36.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project> 这是单独关于gRPC接口proto文件定义的工程,定义后编译工程,maven就会根据前面定义的paycore.proto文件生成gRPC服务端/客户端相关代码。 完成后,继续构建micro-pay微服务的spring boot工程代码,并在其pom.xml文件中引入上述gRPC协议文件定义的依赖,例如: <!--引入支付服务gRPC ProtoBuf定义依赖--> <dependency> <groupId>com.wudimanong</groupId> <artifactId>micro-pay-client</artifactId> <version>1.0-SNAPSHOT</version> </dependency> 在micro-pay-client工程中所引入的gRPC相关的依赖及插件配置会自动继承至micro-pay工程! 2、编写gRPC支付服务代码 在micro-pay代码工程中创建一个PayCoreProvider接口代码,用于表示支付gRPC服务的入口(类似于Controller),其代码如下: package com.wudimanong.micro.pay.provider; import com.wudimanong.micro.pay.proto.PayRequest; import com.wudimanong.micro.pay.proto.PayResponse; import com.wudimanong.micro.pay.proto.PayServiceGrpc; import io.grpc.stub.StreamObserver; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Slf4j @Component public class PayCoreProvider extends PayServiceGrpc.PayServiceImplBase { /** * 实现ProtoBuf中定义的服务方法 * * @param request * @param responseStreamObserver */ @Override public void doPay(PayRequest request, StreamObserver<PayResponse> responseStreamObserver) { //逻辑处理(简单模拟打印日志) log.info("处理gRPC支付处理请求,orderId->{};payAmount{}", request.getOrderId(), request.getAmount()); //构建返回对象(构建处理状态) PayResponse response = PayResponse.newBuilder().setStatus(2).build(); //设置数据响应 responseStreamObserver.onNext(response); responseStreamObserver.onCompleted(); } } 上述代码所引入的一些依赖代码如PayServiceGrpc等,就是前面定义paycore.proto文件所生成的桩文件代码!由于只是简单测试,这里仅仅打印了下日志就返回了,如果涉及复杂业务还是可以按照MVC分层架构思想进行代码拆分! 3、编写gRPC与Spring Boot框架集成配置代码 在Spring Cloud微服务中集成gRPC可以通过前面提到的“grpc-client-spring-boot-starter”来实现,但目前还没有现成的支持Service Mesh架构下的集成SDK,所以这里通过手工配置定义的方式实现集成。先创建一个配置类,代码如下: package com.wudimanong.micro.pay.config; import com.wudimanong.micro.pay.provider.PayCoreProvider; import io.grpc.Server; import io.grpc.ServerBuilder; import java.io.IOException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Slf4j @Component public class GrpcServerConfiguration { @Autowired PayCoreProvider service; /** * 注入配置文件中的端口信息 */ @Value("${grpc.server-port}") private int port; private Server server; public void start() throws IOException { // 构建服务端 log.info("Starting gRPC on port {}.", port); server = ServerBuilder.forPort(port).addService(service).build().start(); log.info("gRPC server started, listening on {}.", port); // 添加服务端关闭的逻辑 Runtime.getRuntime().addShutdownHook(new Thread(() -> { log.info("Shutting down gRPC server."); GrpcServerConfiguration.this.stop(); log.info("gRPC server shut down successfully."); })); } private void stop() { if (server != null) { // 关闭服务端 server.shutdown(); } } public void block() throws InterruptedException { if (server != null) { // 服务端启动后直到应用关闭都处于阻塞状态,方便接收请求 server.awaitTermination(); } } } 如上所示,在该配置代码中,通过gRPC-Java依赖所提供的Server对象构建了gRPC服务端启动、停止、阻塞的方法,并在启动时将前面定义的服务端类通过“.addService()”方法进行了加入(可考虑封装更优雅的方式)! 为了让该配置类与Spring Boot集成,再定义一个集成类,代码如下: package com.wudimanong.micro.pay.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class GrpcCommandLineRunner implements CommandLineRunner { @Autowired GrpcServerConfiguration configuration; @Override public void run(String... args) throws Exception { configuration.start(); configuration.block(); } } 上述代码会在Spring Boot应用启动时自动加载,其中的逻辑就是启动gRPC服务,并阻塞等待连接! 接下来在配置文件中定义服务所开启的gRPC端口,配置如下: spring: application: name: micro-pay server: port: 9092 #定义gRPC服务开放的端口 grpc: server-port: 18888 该配置所定义的参数在前面的服务配置类中引用,表示gRPC服务开启的端口,这里定义的是18888! 到这里gRPC服务端工程代码就构建完成了,从整体上看就是Spring Boot+gRPC的集成与整合,这其中没有引入Spring Boot定制的gRPC集成SDK,目的在于避免其中所涉及的客户端服务治理逻辑(与前面Http调用不直接引入Open Feign一样)。 构建gRPC客户端程序(micro-order) 接下来我们改造micro-order微服务,使其成为调用micro-pay微服务的gRPC客户端程序! 1、引入gRPC客户端依赖包 引入前面定义micro-pay gRPC服务时构建的micro-pay-client protobuf工程依赖,代码如下: <!--引入支付服务gRPC ProtoBuf定义依赖--> <dependency> <groupId>com.wudimanong</groupId> <artifactId>micro-pay-client</artifactId> <version>1.0-SNAPSHOT</version> </dependency> 2、业务逻辑中实现gRPC服务调用 接下来在micro-order逻辑中调用gRPC支付服务,代码示例如下: @Slf4j @Service public class OrderServiceImpl implements OrderService { /** * 引入gRPC客户端配置依赖 */ @Autowired GrpcClientConfiguration gRpcClent; @Override public CreateOrderBO create(CreateOrderDTO createOrderDTO) { log.info("现在开始处理下单请求....."); //生成订单号 String orderId = String.valueOf(new Random(100).nextInt(100000) + System.currentTimeMillis()); //构建支付请求(gRPC调用) PayRequest payRequest = PayRequest.newBuilder().setOrderId(orderId).setAmount(createOrderDTO.getAmount()) .build(); //使用stub发送请求到服务端 PayResponse payResponse = gRpcClent.getStub().doPay(payRequest); log.info("pay gRpc response->" + payResponse.toString()); return CreateOrderBO.builder().orderId(orderId).status(payResponse.getStatus()).build(); } } 如上所示,该业务逻辑在接收micro-api通过Http调用的请求后,会在逻辑实现过程中通过gRPC协议访问支付服务,其中涉及的接口定义代码,由protobuf文件所定义! 3、gRPC客户端配置 上述逻辑是通过定义“GrpcClientConfiguration”gRPC客户端配置类来实现gRPC服务调用的,该配置类代码如下: @Slf4j @Component public class GrpcClientConfiguration { /** * 支付gRPC Server的地址 */ @Value("${server-host}") private String host; /** * 支付gRPC Server的端口 */ @Value("${server-port}") private int port; private ManagedChannel channel; /** * 支付服务stub对象 */ private PayServiceGrpc.PayServiceBlockingStub stub; public void start() { //开启channel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(); //通过channel获取到服务端的stub stub = PayServiceGrpc.newBlockingStub(channel); log.info("gRPC client started, server address: {}:{}", host, port); } public void shutdown() throws InterruptedException { //调用shutdown方法后等待1秒关闭channel channel.shutdown().awaitTermination(1, TimeUnit.SECONDS); log.info("gRPC client shut down successfully."); } public PayServiceGrpc.PayServiceBlockingStub getStub() { return this.stub; } } 如上所示配置代码,通过依服务配置文件指定的gRPC服务端地址+端口,实现对gRPC客户端的配置,其中主要包括启动和停止方法,并在启动的过程中初始化gRPC服务客户端的桩代码的实例(可考虑更优雅地实现)。 在该配置类中所依赖的gRPC服务端地址+端口配置,依赖于服务配置文件的定义,代码如下: spring: application: name: micro-order server: port: 9091 #支付微服务Grpc服务地址、端口配置 server-host: ${grpc_server_host} server-port: ${grpc_server_port} 如果是本地测试可以直接指定grpc_server_host及端口的值,但在Service Mesh微服务架构中,直接在应用的配置文件中指定其他微服务的地址及端口可能并不是很灵活,这个配置信息将在发布Kubernetes集群时,通过Kubernetes发布文件注入! 为了让gRPC客户端配置与Spring Boot集成,这里也需要定义一个Spring Boot加载类,代码如下: @Component @Slf4j public class GrpcClientCommandLineRunner implements CommandLineRunner { @Autowired GrpcClientConfiguration configuration; @Override public void run(String... args) throws Exception { //开启gRPC客户端 configuration.start(); //添加客户端关闭的逻辑 Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { configuration.shutdown(); } catch (InterruptedException e) { e.printStackTrace(); } })); } } 该代码将在Spring Boot应用自动时自动加载!到这里micro-order gRPC客户端配置就完成了! 将部署服务至Service Mesh架构环境 前面基于“micro-order->micro-pay”微服务间的gRPC调用场景,分别将两个微服务改造成了gRPC服务端/客户端。但此时从代码上是很难看出来它们二者之间应该怎么实现调用!而这也恰恰就印证了Service Mesh架构的优势,服务的发现、及负载均衡调用之类的服务治理逻辑,已经完全不用微服务自己管了! 在Istio中,它们是基于Kubernetes的Service发现机制+Istio-proxy(SideCar代理)来实现的。而具体的操作就是通过微服务Kubernetes服务发布文件的定义,接下来分别定义micro-order及micro-pay的Kubernetes发布文件。 先看下作为gRPC服务端的micro-pay的发布文件(micro-pay.yaml),代码如下: apiVersion: v1 kind: Service metadata: name: micro-pay labels: app: micro-pay service: micro-pay spec: type: ClusterIP ports: - name: http #容器暴露端口 port: 19092 #目标应用端口 targetPort: 9092 #设置gRPC端口 - name: grpc port: 18888 targetPort: 18888 selector: app: micro-pay --- apiVersion: apps/v1 kind: Deployment metadata: name: micro-pay-v1 labels: app: micro-pay version: v1 spec: replicas: 2 selector: matchLabels: app: micro-pay version: v1 template: metadata: labels: app: micro-pay version: v1 spec: containers: - name: micro-pay image: 10.211.55.2:8080/micro-service/micro-pay:1.0-SNAPSHOT imagePullPolicy: Always tty: true ports: - name: http protocol: TCP containerPort: 19092 #指定服务gRPC端口 - name: grpc protocol: TCP containerPort: 18888 如上所示k8s发布文件,主要是定义了Service服务访问资源及Deployment容器编排资源,这两种资源都是Kubernetes的资源类型,在容器编排资源和服务资源中分别定义了gRPC的访问端口,通过这种设置,后续gRPC客户端通过Service资源访问服务时,就能够进行端口映射了! 而其他配置则是基本的Kubernetes发布部署逻辑,其中涉及的镜像,需要在发布之前,通过构建的方式对项目进行Docker镜像打包并上传私有镜像仓库(如果有疑问,可以参考本号之前的文章)。 接下来继续看看作为gRPC客户端的micro-order微服务的k8s发布文件(micro-order.yaml),代码如下: apiVersion: v1 kind: Service metadata: name: micro-order labels: app: micro-order service: micro-order spec: type: ClusterIP ports: - name: http #此处设置80端口的原因在于改造的Mock FeignClient代码默认是基于80端口进行服务调用 port: 80 targetPort: 9091 selector: app: micro-order --- apiVersion: apps/v1 kind: Deployment metadata: name: micro-order-v1 labels: app: micro-order version: v1 spec: replicas: 2 selector: matchLabels: app: micro-order version: v1 template: metadata: labels: app: micro-order version: v1 spec: containers: - name: micro-order image: 10.211.55.2:8080/micro-service/micro-order:1.0-SNAPSHOT imagePullPolicy: Always tty: true ports: - name: http protocol: TCP containerPort: 19091 #环境参数设置(设置微服务返回gRPC服务端的地址+端口) env: - name: GRPC_SERVER_HOST value: micro-pay - name: GRPC_SERVER_PORT value: "18888" 在该发布文件中,需要说明的主要就是通过容器env环境参数的设置,指定了之前gRPC客户端服务配置中所依赖的参数变量“GRPC_SERVER_HOST及GRPC_SERVER_PORT”,其中服务地址就是micro-pay微服务在Kubernetes中Service资源定义的名称,端口则是gRPC服务端所开启的端口。 这样在gRPC客户端在Kubernetes集群中根据Service名称发起微服务调用时,Kubernetes集群自身的服务发现逻辑就能自动将请求映射到相应的Pod资源了!这其实就是Service Mesh微服务架构服务发现的基本逻辑! 接下来将微服务进行发布,这里假设你已经部署了一套Kubernetes集群并安装了基于Istio的Service Mesh微服务架构环境,最终的部署效果如下所示: root@kubernetes:/opt/istio/istio-1.8.4# kubectl get pods NAME READY STATUS RESTARTS AGE micro-api-6455654996-9lsxr 2/2 Running 2 43m micro-order-v1-744d469d84-rnqq8 2/2 Running 0 6m28s micro-order-v1-744d469d84-vsn5m 2/2 Running 0 6m28s micro-pay-v1-7fd5dd4768-txq9d 2/2 Running 0 43s micro-pay-v1-7fd5dd4768-wqw6b 2/2 Running 0 43s 如上所示,可以看到案例所涉及的微服务都被部署了,并且对应的SideCar代理(istio-proxy)也被正常启动了!为了演示负载均衡效果,这里micro-order及micro-pay都分别被部署了两个副本! 微服务多副本负载均衡调用演示 如果环境都没啥问题,此时可以通过调用Istio Gateway来访问micro-api服务,然后micro-api服务会通过Http的方式访问micro-order服务,之后micro-order服务通过gRPC协议调用micro-pay服务。 通过curl命令访问Istio Gateway网关服务,效果如下: curl -H "Content-Type:application/json" -H "Data_Type:msg" -X POST --data '{"businessId": "202012102", "amount": 100, "channel": 2}' http://10.211.55.12:30844/api/order/create 如果正常返回响应结果,则说明上述调用链路走通了!此时分别通过观察服务的业务日志和istio-proxy代理日志来加以观测! 其中micro-pay两个实例(PodA~PodB)业务日志信息: //支付微服务接口访问日志(POD-A) root@kubernetes:~# kubectl logs micro-pay-v1-7fd5dd4768-txq9d micro-pay .... 2021-04-01 14:46:15.818 INFO 1 --- [ main] c.w.m.p.config.GrpcServerConfiguration : Starting gRPC on port 18888. 2021-04-01 14:46:18.859 INFO 1 --- [ main] c.w.m.p.config.GrpcServerConfiguration : gRPC server started, listening on 18888. 2021-04-01 15:07:36.709 INFO 1 --- [ault-executor-0] c.w.micro.pay.provider.PayCoreProvider : 处理gRPC支付处理请求,orderId->1617289656289;payAmount100 //支付微服务接口访问日志(POD-B) root@kubernetes:~# kubectl logs micro-pay-v1-7fd5dd4768-wqw6b micro-pay ... 2021-04-01 15:34:59.673 INFO 1 --- [ main] c.w.m.p.config.GrpcServerConfiguration : Starting gRPC on port 18888. 2021-04-01 15:35:06.175 INFO 1 --- [ main] c.w.m.p.config.GrpcServerConfiguration : gRPC server started, listening on 18888. 2021-04-01 15:40:22.019 INFO 1 --- [ault-executor-0] c.w.micro.pay.provider.PayCoreProvider : 处理gRPC支付处理请求,orderId->1617291624127;payAmount100 2021-04-01 15:44:31.630 INFO 1 --- [ault-executor-2] c.w.micro.pay.provider.PayCoreProvider : 处理gRPC支付处理请求,orderId->1617291867537;payAmount100 可以看到,多次访问接口,基于gRPC的微服务调用也实现了负载均衡调用!接下来分别看下这两个微服务的istio-proxy(SideCar代理)的日志,具体如下: --istio-proxy代理日志(POD-A) root@kubernetes:~# kubectl logs micro-pay-v1-7fd5dd4768-txq9d istio-proxy ... 2021-04-01T15:34:48.009972Z info Envoy proxy is ready [2021-04-01T15:40:26.240Z] "POST /com.wudimanong.pay.client.PayService/doPay HTTP/2" 200 - "-" 22 7 498 477 "-" "grpc-java-netty/1.36.1" "8eb318e5-ac09-922d-9ca7-603a5c14bdd5" "micro-pay:18888" "127.0.0.1:18888" inbound|18888|| 127.0.0.1:57506 10.32.0.10:18888 10.32.0.12:36844 outbound_.18888_._.micro-pay.default.svc.cluster.local default 2021-04-01T15:45:18.377555Z info xdsproxy disconnected ... [2021-04-01T15:45:34.885Z] "POST /com.wudimanong.pay.client.PayService/doPay HTTP/2" 200 - "-" 22 7 1200 171 "-" "grpc-java-netty/1.36.1" "c08d540e-db46-9228-b381-0808ac08377e" "micro-pay:18888" "127.0.0.1:18888" inbound|18888|| 127.0.0.1:33218 10.32.0.10:18888 10.32.0.2:42646 outbound_.18888_._.micro-pay.default.svc.cluster.local default ... 2021-04-01T15:52:49.825955Z info xdsproxy connecting to upstream XDS server: istiod.istio-system.svc:15012 如上所示,可以看到istio-proxy代理日志中显示了通过post方式转发gRPC服务的情况,而且可以看出gRRPC是采用Http/2实现的! 后记 本文通过实战案例,演示了在Service Mesh微服务架构下,服务间通过gRPC协议实现通信调用的场景! 欢迎大家关注我的公众号【风平浪静如码】,海量Java相关文章,学习资料都会在里面更新,整理的资料也会放在里面。 觉得写的还不错的就点个赞,加个关注呗!点关注,不迷路,持续更新!!!

优秀的个人博客,低调大师

YuebonNetCore v1.0.5 已经发布,权限管理及快速开发框架

YuebonNetCore v1.0.5 已经发布。首先恭祝所有关注YuebonCore的朋友新春快乐!牛年大吉! 在此新年之际 , 我们发布了v1.0.5版 , 这次版本发布的最大变化实现数据库读写分离和新增请求签名验证。 新特性 1、【新增】实现数据库读写分离,支持一主多从读写分离 2、【新增】增加浮点数转换器DecimalJsonConverter,在转换json时需用 3、【新增】新增请求签名验证,防止请求参数是否被篡改,提高接口安全性 4、【新增】添加swagger header是否需要签名验证 5、【新增】富文本编辑器tinymce 突破性变化 1、【调整】优化文章管理模块,富文本编辑器集成tinymce,汉化和实现本地化图片上传 2、【调整】调整定时任务模块为独立类库 3、【调整】调整数据类型枚举 4、【调整】删除sqldapper类和接口 5、【调整】调整EF oracle 引用包为devart.data.oracle.efcore 问题修复 1、【优化】优化超级管理员不分配权限可以拥有所有操作权限 2、【优化】上传图片优化 3、【优化】修复mysql数据库情况下根据部门id获取组织根节点错误 4、【优化】修复vue页面代码模板 5、【优化】修复定时任务异常 6、【优化】修复新增、修改数据字典分类判断分类编码是否重复 7、【优化】修复当弹出框高度大于屏幕显示高度时,拖拽时,弹出框的顶端会超出屏幕的上端,造成无法再拖拽 8、【优化】修复代码生成器生成前端vue代码删除数据错误问题 9、【优化】修复获取文章分类树形列表,判断为空的问题 其他更改 1、发布使用视频 2、完善部分方法注释 详情查看:https://gitee.com/yuebon/YuebonNetCore/releases/v1.0.5

优秀的个人博客,低调大师

如何利用go-zero在Go中快速实现JWT认证

关于JWT是什么,大家可以看看官网,一句话介绍下:是可以实现服务器无状态的鉴权认证方案,也是目前最流行的跨域认证解决方案。 要实现JWT认证,我们需要分成如下两个步骤 客户端获取JWT token。 服务器对客户端带来的JWT token认证。 1. 客户端获取JWT Token 我们定义一个协议供客户端调用获取JWT token,我们新建一个目录jwt然后在目录中执行 goctl api -o jwt.api,将生成的jwt.api改成如下: type JwtTokenRequest struct { } type JwtTokenResponse struct { AccessToken string `json:"access_token"` AccessExpire int64 `json:"access_expire"` RefreshAfter int64 `json:"refresh_after"` // 建议客户端刷新token的绝对时间 } type GetUserRequest struct { UserId string `json:"userId"` } type GetUserResponse struct { Name string `json:"name"` } service jwt-api { @handler JwtHandler post /user/token(JwtTokenRequest) returns (JwtTokenResponse) } @server( jwt: JwtAuth ) service jwt-api { @handler JwtHandler post /user/info(GetUserRequest) returns (GetUserResponse) } 在服务jwt目录中执行:goctl api go -api jwt.api -dir . 打开jwtlogic.go文件,修改 func (l *JwtLogic) Jwt(req types.JwtTokenRequest) (*types.JwtTokenResponse, error) { 方法如下: func (l *JwtLogic) Jwt(req types.JwtTokenRequest) (*types.JwtTokenResponse, error) { var accessExpire = l.svcCtx.Config.JwtAuth.AccessExpire now := time.Now().Unix() accessToken, err := l.GenToken(now, l.svcCtx.Config.JwtAuth.AccessSecret, nil, accessExpire) if err != nil { return nil, err } return &types.JwtTokenResponse{ AccessToken: accessToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil } func (l *JwtLogic) GenToken(iat int64, secretKey string, payloads map[string]interface{}, seconds int64) (string, error) { claims := make(jwt.MapClaims) claims["exp"] = iat + seconds claims["iat"] = iat for k, v := range payloads { claims[k] = v } token := jwt.New(jwt.SigningMethodHS256) token.Claims = claims return token.SignedString([]byte(secretKey)) } 在启动服务之前,我们需要修改etc/jwt-api.yaml文件如下: Name: jwt-api Host: 0.0.0.0 Port: 8888 JwtAuth: AccessSecret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx AccessExpire: 604800 启动服务器,然后测试下获取到的token。 ➜ curl --location --request POST '127.0.0.1:8888/user/token' {"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDEyNjE0MjksImlhdCI6MTYwMDY1NjYyOX0.6u_hpE_4m5gcI90taJLZtvfekwUmjrbNJ-5saaDGeQc","access_expire":1601261429,"refresh_after":1600959029} 2. 服务器验证JWT token 在api文件中通过jwt: JwtAuth标记的service表示激活了jwt认证。 可以阅读rest/handler/authhandler.go文件了解服务器jwt实现。 修改getuserlogic.go如下: func (l *GetUserLogic) GetUser(req types.GetUserRequest) (*types.GetUserResponse, error) { return &types.GetUserResponse{Name: "kim"}, nil } 我们先不带JWT Authorization header请求头测试下,返回http status code是401,符合预期。 ➜ curl -w "\nhttp: %{http_code} \n" --location --request POST '127.0.0.1:8888/user/info' \ --header 'Content-Type: application/json' \ --data-raw '{ "userId": "a" }' http: 401 加上Authorization header请求头测试。 ➜ curl -w "\nhttp: %{http_code} \n" --location --request POST '127.0.0.1:8888/user/info' \ --header 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDEyNjE0MjksImlhdCI6MTYwMDY1NjYyOX0.6u_hpE_4m5gcI90taJLZtvfekwUmjrbNJ-5saaDGeQc' \ --header 'Content-Type: application/json' \ --data-raw '{ "userId": "a" }' {"name":"kim"} http: 200 综上所述:基于go-zero的JWT认证完成,在真实生产环境部署时候,AccessSecret, AccessExpire, RefreshAfter根据业务场景通过配置文件配置,RefreshAfter 是告诉客户端什么时候该刷新JWT token了,一般都需要设置过期时间前几天。 3. 项目地址 https://github.com/tal-tech/go-zero 好未来技术

优秀的个人博客,低调大师

鸿蒙开发快速入门之:Hi3861 开发板(1)

Hi3861开发板介绍 Hi3861 WLAN模组是一片大约2cm*5cm大小的开发板,是一款高度集成的2.4GHz WLAN SoC芯片,集成IEEE 802.11b/g/n基带和RF(Radio Frequency)电路。支持OpenHarmony,并配套提供开放、易用的开发和调试运行环境。 图 1Hi3861 WLAN模组外观图 另外,Hi3861 WLAN模组还可以通过与Hi3861底板连接,扩充自身的外设能力,底板如下图所示。 图 2Hi3861底板外观图 RF电路包括功率放大器PA(Power Amplifier)、低噪声放大器LNA(Low Noise Amplifier)、RF Balun、天线开关以及电源管理等模块;支持20MHz标准带宽和5MHz/10MHz窄带宽,提供最大72.2Mbit/s物理层速率。 Hi3861 WLAN基带支持正交频分复用(OFDM)技术,并向下兼容直接序列扩频(DSSS)和补码键控(CCK)技术,支持IEEE 802.11 b/g/n协议的各种数据速率。 Hi3861芯片集成高性能32bit微处理器、硬件安全引擎以及丰富的外设接口,外设接口包括SPI(Synchronous Peripheral Interface)、UART(Universal Asynchronous Receiver & Transmitter)、I2C(The Inter Integrated Circuit)、PWM(Pulse Width Modulation)、GPIO(General Purpose Input/Output)和多路ADC(Analog to Digital Converter),同时支持高速SDIO2.0(Secure Digital Input/Output)接口,最高时钟可达50MHz;芯片内置SRAM(Static Random Access Memory)和Flash,可独立运行,并支持在Flash上运行程序。 Hi3861芯片适用于智能家电等物联网智能终端领域。 图 3Hi3861功能框图 资源和约束 Hi3861 WLAN模组资源十分有限,整板共2MB FLASH,352KB RAM。在编写业务代码时,需注意资源使用效率。 开发板规格 表 1Hi3861 WLAN模组规格清单 规格类型 规格清单 通用规格 1×1 2.4GHz频段(ch1~ch14) PHY支持IEEE 802.11b/g/n MAC支持IEEE802.11 d/e/h/i/k/v/w 内置PA和LNA,集成TX/RX Switch、Balun等 支持STA和AP形态,作为AP时最大支持6 个STA接入 支持WFA WPA/WPA2 personal、WPS2.0 支持与BT/BLE芯片共存的2/3/4 线PTA方案 电源电压输入范围:2.3V~3.6V IO电源电压支持1.8V和3.3V 支持RF自校准方案 低功耗: Ultra Deep Sleep模式:5μA@3.3V DTIM1:1.5mA@3.3V DTIM3:0.8mA@3.3V PHY特性 支持IEEE802.11b/g/n单天线所有的数据速率 支持最大速率:72.2Mbps@HT20 MCS7 支持标准20MHz带宽和5M/10M窄带宽 支持STBC 支持Short-GI MAC特性 支持A-MPDU,A-MSDU 支持Blk-ACK 支持QoS,满足不同业务服务质量需求 CPU子系统 高性能 32bit微处理器,最大工作频率160MHz 内嵌SRAM 352KB、ROM 288KB 内嵌 2MB Flash 外围接口 1个SDIO接口、2个SPI接口、2个I2C接口、3个UART接口、15个GPIO接口、7路ADC输入、6路PWM、1个I2S接口(注:上述接口通过复用实现) 外部主晶体频率40M或24M 其他信息 封装:QFN-32,5mm×5mm 工作温度:-40℃~+85℃ OpenHarmony关键特性 OpenHarmony基于Hi3861平台提供了多种开放能力,提供的关键组件如下表所示。 表 2OpenHarmony关键组件列表 组件名 能力介绍 WLAN服务 提供WLAN服务能力。包括:station和hotspot模式的连接、断开、状态查询等。 模组外设控制 提供操作外设的能力。包括:I2C、I2S、ADC、UART、SPI、SDIO、GPIO、PWM、FLASH等。 分布式软总线 在OpenHarmony分布式网络中,提供设备被发现、数据传输的能力。 设备安全绑定 提供在设备互联场景中,数据在设备之间的安全流转的能力。 基础加解密 提供密钥管理、加解密等能力。 系统服务管理 系统服务管理基于面向服务的架构,提供了OpenHarmony统一化的系统服务开发框架。 启动引导 提供系统服务的启动入口标识。在系统服务管理启动时,调用boostrap标识的入口函数,并启动系统服务。 系统属性 提供获取与设置系统属性的能力。 基础库 提供公共基础库能力。包括:文件操作、KV存储管理等。 DFX 提供DFX能力。包括:流水日志、时间打点等。 XTS 提供OpenHarmony生态认证测试套件的集合。

优秀的个人博客,低调大师

三分钟快速了解数据库

在现今互联网企业坐拥着爆炸式数据的信息时代,数据库显得尤为重要重要。那什么是数据库呢?今天就简单给大家讲讲什么是数据库,花三分钟时间看完,你就会对它有个基础的了解啦。 一、什么是数据库? 首先在了解数据库之前,我们要先了解一下数据是怎么储存的。 从前到现在,人类经历了结绳记事,甲骨、竹简、纸张记录,录音机、摄像机储存等方法来进行数据储存,这些都属于传统存储方式。 而信息时代的到来,数据存储的方式发生了重大变革,朝着两分方向发展:文件与数据库。 1、文件就是把数据存放在Excel中,形成读写文件后进行存储,然后通过python等工具对文件数据进行筛选、处理、提取; 2、数据库则是把数据按照其结构将储存在计算机中,形成一个具有大数据量的数据集合,像是一个存放文件的文件柜。 利用数据库存储数据是目前最为通用且流行的方式,因为数据库拥有持久化存储、读写速度也很高,并且数据库能极大程度保证数据有效性,不会出现Excel等文件的修改错误的情况。 二、数据库的分类 目前最常用的数据库模式主要有两种,即关系型数据库和非关系型数据库。 1、关系型数据库 关系型数据库模型是把复杂的数据结构归结为简单的二元关系。在关系型数据库中,对数据的操作几乎全部建立在一个或多个关系表格上,通过这些关联的表格分类、合并、连接或选取等运算来实现数据的管理。对于关系型的通俗理解,即数据作为二维数组存在,就像图书馆的图书排列。 书是数据,书架、楼层是关系型的数据结构,而图书馆管理员就是数据库的进程,用于不同的工作。有人救火(数据修复、备份),有人整理书架、书籍(数据整理、归档),而用户进程就是指的来到图书馆的顾客,他们看书、移动书籍。 关系型数据库常见的有MySQL和Oracle,oracle。 2、非关系型数据库 随着web2.0网站的兴起,关系型数据库无法满足海量数据对存储容量的要求,而需要用集群来解决问题,因此非关系型数据库应运而生。 非关系型数据库也被称为NoSQL数据库,它针对特定场景,以高性能和使用便利为目的功能特异化的数据库产品,比如Google的BigTable与Amazon的Dynamo。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册