本文可分为三个部分:基本产品介绍、Dubbo对gRPC (HTTP/2)和Protobuf的支持以及示例演示。引言部分介绍了杜博、HTTP/2、gRPC和Protobuf的基本概念和特点。第二部分介绍了为什么Dubbo支持gRPC (HTTP/2)和Protobuf,以及这种支持给gRPC和Dubbo的发展带来的好处和区别。第三部分分别通过两个例子说明了杜博gRPC和杜博Protobuf的用法。
基本介绍Dubbo协议< p>
从协议级别扩展而来。2.7版目前支持以下Dubbo协议:
众所周知,Dubbo协议是在传输控制协议传输层协议之上直接定义的。由于TCP的高可靠性和全双工特性,它为Dubbo协议的定义提供了最大的灵活性。然而,也正是因为这种灵活性,RPC协议通常是一种定制的私有协议。杜博也面临这个问题。在这里,我们将集中讨论在协议通用性方面,Dubbo值得改进的地方。有关协议的详细分析,请参考官方网站博客。
Dubbo协议主体有一个可扩展的附件部分,这使得除了RPC方法之外还可以传递其他属性,这是一个很好的设计。但是,类似的标题部分缺少类似的可扩展附件,这可以参考由HTTP定义的Ascii标题设计来划分正文附件和标题附件之间的责任。主体协议主体中的一些RPC请求定位器,如服务名、方法名、版本等。,可以在Header中提及,并与特定的序列化协议分离,以便网络基础架构更好地识别或用于流量控制。可伸缩性不够好,没有为协议升级设计,比如报头中没有预留状态标识位,或者有专门为协议升级或协商设计的特殊数据包,比如HTTP。在Java版本的代码实现中,不够简单和通用例如,在链接传输中,有一些语言绑定的内容;邮件正文中存在冗余内容,如正文和附件中的服务名。HTTP/1
比直接构建带TPC传输层的私有RPC协议具有更好的通用性。基于HTTP构建的远程调用解决方案具有更好的通用性,如web服务或REST架构。使用HTTP JSON可以说是一个事实上的标准解决方案。
的所有选项都是基于HTTP构建的,我认为有两个最大的优势:
HTTP的语义和可扩展性能能够很好地满足RPC调用的要求。通用性,网络上几乎所有的设备都支持HTTP协议,并且具有良好的协议穿透性。
具体来说,HTTP/1的优点和局限性是:
在典型的请求-响应模型中,一个链接一次只能有一个请求请求等待。 HTTP/1支持保活链接,避免了重复创建链接的开销。 人类可读的标题,使用更通用和更容易阅读的标题传输格式 没有直接的服务器推送支持,需要轮询长轮询等替代模式。HTTP/2
HTTP/2保留了HTTP/1的所有语义,在保持兼容性的同时,在通信模型和传输效率上有了很大的改进。
gRPC
上面提到的建立在协议基础上的远程过程控制协议的优点和缺点。与建立在TPC传输层上的Dubbo相比,谷歌选择了在HTTP/2协议上直接定义gRPC。关于gRPC的基本介绍和设计理念,请参考以上两篇文章。我将只挑出几个能反映gRPC设计目的的设计视觉特征在这里做一个简要的说明
gRPC:
的基本介绍https://platformlab . Stanford . edu/Seminar Talks/GRPc . pdf
设计愿景:
https://grpc . io/blog/principles/?SPM = ATA . 13261165 . 0 . 0 . 2 be 55017 xbus 8
覆盖范围和简单性、协议设计和框架实现应该足够通用和简单,可以在任何设备上运行,甚至是一些资源优先,如物联网、移动和其他设备互操作性和可及性,为了构建一个更通用的协议,协议本身必须得到网络上几乎所有基础设施的支持通用&性能,为了在场景和性能之间取得良好的平衡,首先,协议本身应该适用于各种场景,同时,它应该具有尽可能高的性能。负载不可知,协议上传输的负载在语言和平台上应该是中性的。流,以支持请求-响应通信模型、请求-流通信模型、双流通信模型等。流量控制,协议本身具有感知和限制流量的能力除了RPC服务定义之外,元数据交换还提供了额外的数据传输功能总的来说,在这种设计理念的指导下,gRPC最终被设计成一个跨语言、跨平台、通用、高性能的基于HTTP/2的RPC协议和框架
Protobuf
协议缓冲区(Protocol Buffers)是由谷歌推出的跨平台、语言中立的结构化数据描述和序列化产品。它定义了一组用于定义结构化数据的协议,还提供了相应的编译器工具来将语言无关的描述转换成相应语言的特定描述。
协议缓冲区(Protobuf)参考:
https://developers . Google . com/protocol-buffers/docs/overview
编译器详细信息参考:
https://github . com/protocol buffers/protobuf/releases/tag/v 3 . 10 . 0
它的一些特性包括:
跨语言、跨平台、语言中立的数据描述格式提供了默认情况下生成多种语言的编译器工具安全性,因为反序列化的范围和输出内容的格式是由编译器在编译时预先生成的,所以像Java反序列化漏洞这样的问题被忽略了。二进制高性能 强型 字段更改是向后兼容的 消息个人{ 必需的字符串名称= 1;必需的int 32 id = 2;可选字符串email = 3;枚举PhoneType { MobiLe = 0;HOME = 1;工作= 2; }消息电话号码{ 必需的字符串号码= 1;可选电话类型= 2[默认值=家庭]; }重复电话号码电话= 4;}除了结构化数据描述,Protobuf还支持RPC服务的定义,这允许我们定义的服务描述文件。然后使用Protobuf编译器工具为特定的语言和RPC框架生成接口和存根。稍后将详细描述的GRPC原型、杜博-gRPC原型和杜博原型都是通过定制编译器类实现的。
服务搜索服务{ rpc搜索(搜索请求)返回(搜索响应);} 杜博的支持的跨语言服务开发涉及许多方面,从服务定义、RPC协议到序列化协议,所有这些都应该是语言中立的,同时每种语言都应该有相应的SDK实现。尽管由于社区的贡献,Dubbo在多语言SDK的实现方面已经逐渐改进,并且已经提供了客户端或全面的实现版本,包括Java、Go、PHP、C#、Python、NodeJs、C等。在上述跨语言友好方面仍有许多改进。
协议,以上我们分析了Dubbo协议存在的缺点。如果应用层协议能够建立在HTTP/2之上,无疑将避免这些缺点,同时,它将尽可能提高协议的穿透性,避免网关等协议转换组件的存在,更有利于链路上的流量控制。考虑到gRPC是基于HTTP/2构建的,并且已经是云本地域中推荐的通信协议,Dubbo在第一阶段选择直接支持gRPC协议作为当前的HTTP/2解决方案。我们也知道gRPC框架本身的缺点在于缺乏可用性和服务治理能力(这也是大多数制造商不直接使用gRPC框架的原因)。通过将其集成到Dubbo框架中,用户可以方便地使用Dubbo编程模型Dubbo服务来管理gRPC协议的组合通信。服务定义,目前是Dubbo的服务定义和特定的编程语言绑定,不提供语言中立的服务描述格式。例如,Java定义了接口接口,并且必须以其他语言的另一种格式重新定义因此,Dubbo通过支持Protobuf来实现语言中立的服务定义。目前由Dubbo支持的序列化包括Json、Hessian2、Kryo、FST、Java等。其中,只有Json和Hessian2支持跨语言。通用Json有固有的性能问题,而Hessian2既缺乏效率又缺乏多语言SDK因此,Dubbo通过支持Protobuf序列化,提供了一个更高效、更易于使用的跨语言序列化方案。例子示例1,使用Dubbo开发gRPC服务
gRPC是谷歌基于HTTP/2的开源中国通信协议Dubbo依靠其灵活的协议扩展机制来增加对gRPC (HTTP/2)协议的支持
目前对的支持仅限于杜博Java语言版本,后续的Go语言或其他语言版本将以类似的方式提供支持下面是一个简单的例子,演示如何在Dubbo中使用gRPC协议通信。详情请参考
https://github . com/Apache/dubbo-samples/tree/master/dubbo-samples-grpc
1。定义服务IDL
首先,服务由标准Protobuf协议定义如下:
syntax = " proto3选项java _ multiple _ files = trueoption Java _ package = " io . grpc . examples . hello world ";选项Java _ outer _ class name = " HelloworldProto ";选项objc _ class _ prefix = " HLW包helloworld //问候语服务定义。服务问候语{ //发送问候语 rpc问候语(HelloRequest) 返回(HellOrePly){ } } //包含用户名的请求消息。消息HelloRequest { 字符串名称= 1;} //包含问候语的响应消息消息HelloReply { 字符串消息= 1;} 在这里,我们定义了一个只有一个方法的问候服务,并定义了方法的输入和输出参数。 Protobuf编译器生成Stub 定义Maven Protobuf编译器插件工具在这里,我们扩展了Protobuf的编译器工具,以生成特定于Dubbo的RPC存根,该存根目前作为Maven插件发布org . xolstice . maven . plugins protobuf-maven-plugin 0 . 5 . 1 com . Google . protobuf:protoca:3 . 7 . 1:exe:$ { OS . detected . decider } dubbo-grpc-Java9
,其中pluginArtifact指定Dubbo定制版本的Java Protobuf编译器插件,通过该插件在编译期间生成Dubbo定制版本的gRPC存根。
org . Apache . dubbo:protocol-gen-dubbo-Java:1 . 19 . 0-SNAPSHOT:exe:$ { OS . detected . classifier }由于protocol-gen-dubbo-Java支持gRPC和dubbo,默认情况下可以生成的存根类型是gRPC。关于Dubbo协议的使用,请参考使用Protobuf的Dubbo服务的开发。
grpc3。生成Java Bean和Dubbo-gRPC stub
#运行以下maven命令$ mvn干净编译 生成的存根和消息类如下:侧重于GreeterGrpc,它包括所有Grpc标准存根类/方法,并添加了Dubbo特定的接口。之后,提供者端的服务公开和消费者端的服务调用都将依赖于这个接口。
/***为Dubbo */公共接口IGreeter { 默认公共io . grpc . examples . hello world . hellorePly SayHello(io . grpc . examples . hello world . hellorequest请求){ 抛出新的UnsupportedOperationException(“不需要重写此方法,扩展XxxImplBase并重写它允许的所有方法。”); }默认值public com . Google . common . util . concurrent . ListEnableFuture SayHelloasync(io . grpc . examples . hello world . hellorequest request){ 引发新的UnsupportedOperationException(“不需要重写此方法,扩展XxxImplBase并重写它允许的所有方法。”);} public void SayHello(io . grpc . examples . hello world . hellorequest request,io . grpc . stub . StreamObserver responseObserver);}4。业务逻辑开发
继承了GreeterGrpc。GreeterImplBase(来自步骤2)并编写业务逻辑,这与本机gRPC是一致的。
package org . Apache . dubbo . samples . basic . impl;import io . grpc . examples . hello world . greetergrpc;import io . grpc . examples . hello world . HellorePly;import io . grpc . examples . hello world . Hellorequest;import io . grpc . stub . StreamObserver;公共类GrpcGreeterImpl扩展了GreeterGrpc。HelloRequest请求,StreamObserver响应服务器{ System.out.println("从客户端收到请求。"); System.out.println("执行线程是" Thread.currentThread()。getName()); HelloReply回复= HelloReply.newBuilder()。设置消息(“你好”请求。build();ResponseObserver . onNext(回复);responseobserver . oncompleted(); }}5。提供商方公开Dubbo服务
以Spring XML为例:
公共静态void main(字符串[)参数引发异常{ ClassPathXMLAPPlicationContext = new ClassPathXMLAPPlicationContext(" spring/dubbo-demo-provider . XML ");context . start();系统输出打印(“dubbo服务已启动”);新的CountDownLatch(1)。wait();}6。参考杜博服务
公共静态void main(字符串[)参数引发IoException { ClassPathXMLAPPlicationContext = new ClassPathXMLAPPlicationContext(" spring/dubbo-demo-consumer . XML ");context . start(); GreeterGrpc。迎宾员=(迎宾员rpc。问候者 HelloReply回复=问候者.再见(HelloRequest.newBuilder()。setName(“世界!”)。build()); System.out.println(“结果:”reply . GetMessage());系统输入读取();} 附件示例1:高级用法1,异步调用
让我们再来看看协议生成的接口:
/***为Dubbo */公共接口IGreeter { 默认公共HellOrePly SayHello(HellOrequest请求){ //...... }默认公共ListEnableFuture SayHellOasync(HellOrequest请求){ //...... }公共空说你好(HelloRequest请求,流观察器响应服务器);} 这里,为sayHello方法生成了三种类型的重载方法,分别用于同步调用、异步调用和流调用。如果消费者想要进行异步调用,直接调用sayHelloAsync()就足够了: 公共静态void main(字符串[)参数引发IOException { //... GreeterGrpc。迎宾员=(迎宾员rpc。问候者 ListenableFuture未来=迎宾员. SayHasyncello(Hellorequest . NewBuilder()。setName(“世界!”)。build()); //...}2,高级配置
因为当前的实现直接集成了gRPC-java SDK,许多配置没有与Dubbo端对齐,或者没有以Dubbo配置的形式打开。因此,为了提供最大的灵活性,我们直接公开了gRPC-java配置界面。
在大多数情况下,您可能不使用以下扩展,因为它们主要是阻止gRPC协议或HTTP/2级配置。同时使用这些扩展点可能需要对HTTP/2或gRPC有基本的了解
扩展点
目前支持以下扩展点:
org . Apache . dubbo . RPC . protocol . grpc . interceptors . ClientInterceptor org . Apache . dubbo . RPC . protocol . grpc . interceptors . grpcconfigurator org . Apache . dubbo . RPC . protocol . grpc . interceptors . ServerInterceptor org . Apache . dubbo . RPC . protocol . grpc . interceptors . ServerTransportFilterGrpcConfigurator是最常见的扩展点。让我们以此为例来说明。其基本定义如下:
公共接口GRPcConfigurator {//用于自定义GRPC网络服务器生成器默认网络服务器生成器配置服务器生成器(网络服务器生成器,网址){ 返回生成器;} //用于自定义grpcnettychannelbuilder defaultnettychannelbuilder configurechannelbuilder(nettychannelbuilder,URL){ return builder;} //用于自定义gRPC调用选项,并定义在每个请求之间传递数据的服务。defaultcalloptions配置选项(网址){ return options; }} 下面是一个扩展实现的例子: 公共类MyGrpcConfigurator实现GrpcConfigurator { 私有最终Executorservice executor = Executors . NewFixedThreadPool(200,新名称ThreadFactory(“Customized-grpc”,true));@ Override 公共NettyServerBuilder配置服务器生成器(NettyServerBuilder生成器,网址){ 返回生成器. executor(执行程序);} @ Override public NettyChannelBuilder ConfigureChannelBuilder(NettyChannelBuilder生成器,URL){ return Builder . FlowControlWindow(10);} @覆盖公共呼叫选项配置呼叫选项(呼叫选项,网址){ 返回选项。关键。创造(“关键”),“价值”); }} 配置为dubospi,“资源/元信息/服务添加配置文件”default = org . Apache . dubbo . samples . basic . com tomize . MyGrpcConfigurator1。默认情况下,指定的提供者端线程池< p>
使用Dubbo的线程池,具有固定(默认)、缓存、直接和其他类型下面演示了切换到业务自定义线程池 私有最终Executorservice executor = Executors . NewFixedThreadPool(200,新名称ThreadFactory(“Customized-grpc”,true));公共网络服务器生成器配置服务器生成器(网络服务器生成器,网址){返回生成器.执行器(执行器);}2。指定用户终端电流限值将用户电流限值设置为10
@ Override public NettyCHannelBuilder configureChannelBuilder(NettyCHannelBuilder生成器,URL){ return builder . FlowControlWindow(10);}3。传递附加参数
演示服务服务呼叫传递密钥
@覆盖公共呼叫选项配置呼叫选项(呼叫选项选项,网址){ if(网址. getServiceInterface()。等于(“xxx。DeMoServiCe "){ 返回选项. withOption(调用选项。关键。创造(“关键”),“价值”); }其他{ 返回选项; }}3,双向流
代码还提供了一个支持双向流通信的示例,以及一个拦截流调用的拦截器扩展的示例实现。
*在客户端工作的MyClientStreamInterceptor拦截发送的请求流和接收的响应流
*在服务器端工作的MyServerStreamInterceptor拦截收到的请求流和发送的响应流双向流通信示例,
详见:http://github . com/Apache/dubbo-samples/tree/master/dubbo-samples-grpc/src/main/Java/org/Apache/dubbo/samples/basic/impl/routeguid
4,TLS配置
的配置方法与Dubbo提供的[通用顶级域名系统支持]()一致。请参考文件
示例2,使用Protobuf开发Dubbo服务
接下来,让我们看一下基于Protobuf的Dubbo服务开发过程,并给出一个[的具体例子]()。
1。定义服务通过标准的Protobuf定义服务
syntax = " proto3选项java _ multiple _ files = trueoption Java _ package = " org . Apache . dubbo . demo ";选项Java _ outer _ class name = " DemoServiceProto ";option objc _ class _ prefix = " DEMOSRV ";包demoservice //演示服务定义。服务演示服务{ RPC SayHello(HellOrequest)返回(HellOrePly){ } } //包含用户名的请求消息。消息HelloRequest { 字符串名称= 1; } //包含问候语的响应消息消息HelloReply { 字符串消息= 1; } 这里定义了一个DemoService服务。该服务只包括一个sayHello方法,该方法的输入和输出参数是同时定义的。编译器编译服务 引入Protobuf编译器Maven插件,并同时指定协议扩展org . xolstice . maven . plugins protobuf-maven-plugin 0 . 5 . 1 com . Google . protobuf:protoca:3 . 7 . 1:exe:$ { OS . detected . decider } dubbo-grpc-Java9 请注意,dubbo支持gRPC的不同之处在于:Dubbo
2,生成rpcsub
#运行以下maven命令$mvn干净编译 生成的Java类如下: 公共最终类DemoServiceDubBo { 私有静态最终原子池已注册=新原子池();私有静态类init() {类clazz = null请尝试{ clazz = Class . ForName(DemoServiceDuBo . class . GetName());if(registered . CompareAndSet(false,true)){ org . Apache . dubbo . common . serialize . protobuf . support . Protobuutils . marshaller(org . Apache . dubbo . demo . HellOrequest . GetDefaultInstance());org . Apache . dubbo . common . serialize . protobuf . support . Protobuutils . marshaller(org . Apache . dubbo . demo . helloreply . GetDefaultInstance());} }捕获(ClassNotFoundException e) { //忽略}返回clazz} 私有DemoServiceDuBo(){ } 公共静态最终字符串SERVICE_NAME = "demoservice。“demo service”;/** *为Dubbo */公共接口IDemoService生成的代码{静态类clazz = init();org . Apache . dubbo . demo . HellOrePly SayHello(org . Apache . dubbo . demo . HellOrequest请求);Java . util . concurrent . CompletableFuture SayHelloasync(org . Apache . dubbo . demo . Hellorequest请求);}}最值得注意的方面是IDemoService接口,它将作为Dubbo服务定义的基础接口。
3,开发业务逻辑
从这一步开始,所有开发过程都与直接定义Java接口相同实现接口定义业务逻辑
公共类DemoServiceImpl实现DemoServiceDubbo。同上服务{ 私有静态最终记录器记录器记录器=记录器工厂. GetLogger(DemoServiceImpl . class);@ Override public HellOrePly SayHello(HellOrequest请求){ logger . info(" Hello " request . GetName()),来自消费者的请求:" RpcContext.getContext()。getRemoteAddress());返回HelloReply.newBuilder()。设置消息(“你好”请求。getName()),来自提供程序的响应:“RpcContext.getContext()。getLocalAddress())。build();} @ Override public CompletableFuture SayHellLoasync(HellOrequest请求){ return CompletableFuture . CompletedFuture(SayHello(请求)); }}4,配置提供程序
公开Dubbo服务
公共静态void main(字符串[)参数引发异常{ ClassPathXMLAPPlicationContext = new ClassPathXMLAPPlicationContext(" spring/dubbo-provider . XML ");context . start();系统输入读取();}5,配置消费者
引用Dubbo服务
公共静态void main(字符串[)参数引发异常{ ClassPathXMLAPPlicationContext = new ClassPathXMLAPPlicationContext(" spring/dubbo-consumer . XML ");context . start();同上服务DeMoServiCe = context . GetBean(“DeMoServiCe”,同上服务. class); HelloRequest请求= HelloRequest.newBuilder()。设置名称(“你好”)。build(); HelloReply回复= demoService.sayHello(请求); System.out.println(“结果:”reply . GetMessage());系统输入读取();} 观点RPC协议是实现微服务系统互操作的核心组件。通常,使用不同的微服务通信框架意味着绑定特定的协议。例如,Spring Cloud通过基于HTTP的HTTP/2提供gRPC,GRPC通过HTTP/2提供gRPC,节俭黑森等等都是定制的私有协议。
Dubbo本身也提供了私有的Dubbo协议,因此您可以基于Dubbo协议构建微服务。然而,除了单一协议之外,与所有上述框架不同,Dubbo的最大优势在于它能够同时支持多个协议的公开和消费。与Dubbo的多注册订阅模型相结合,Dubbo可以成为桥接多种不同协议的微服务系统的开发框架,并轻松实现不同微服务系统的互调或技术堆栈迁移。
文章详细解释了Dubbo对gRPC协议的支持,以及Dubbo对REST、Hessian、节俭等的支持。这使得Dubbo具备了协议互调的基础。我们只能通过在服务发现模型上开放这些不同的系统来解决不同技术堆栈的互调和迁移问题。对于这部分的具体应用场景和工作模式,我们将在下面的文章中进行分析。