Dubbo 在跨語言和協(xié)議穿透性方向上的探索:支持 HTTP/2 gRPC 和 Protobuf
本文總體上可分為基礎(chǔ)產(chǎn)品簡介、Dubbo 對 gRPC (HTTP/2) 和 Protobuf 的支持及示例演示三部分,在簡介部分介紹了 Dubbo、HTTP/2、gRPC、Protobuf 的基本概念和特點(diǎn);第二部分介紹了 Dubbo 為何要支持 gRPC (HTTP/2) 和 Protobuf,以及這種支持為 gRPC 和 Dubbo 開發(fā)帶來的好處與不同;第三部分通過兩個(gè)實(shí)例分別演示了 Dubbo gRPC 和 Dubbo Protobuf 的使用方式。
基本介紹
Dubbo 協(xié)議
從協(xié)議層面展開,以下是當(dāng)前 2.7 版本支持的 Dubbo 協(xié)議
眾所周知,Dubbo 協(xié)議是直接定義在 TCP 傳輸層協(xié)議之上,由于 TCP 高可靠全雙工的特點(diǎn),為 Dubbo 協(xié)議的定義提供了最大的靈活性,但同時(shí)也正是因?yàn)檫@樣的靈活性,RPC 協(xié)議普遍都是定制化的私有協(xié)議,Dubbo 同樣也面臨這個(gè)問題。在這里我們著重講一下 Dubbo 在協(xié)議通用性方面值得改進(jìn)的地方,關(guān)于協(xié)議詳細(xì)解析請參見官網(wǎng)博客
Dubbo 協(xié)議體 Body 中有一個(gè)可擴(kuò)展的 attachments 部分,這給 RPC 方法之外額外傳遞附加屬性提供了可能,是一個(gè)很好的設(shè)計(jì)。但是類似的 Header 部分,卻缺少類似的可擴(kuò)展 attachments,這點(diǎn)可參考 HTTP 定義的 Ascii Header 設(shè)計(jì),將 Body Attachments 和 Header Attachments 做職責(zé)劃分。
Body 協(xié)議體中的一些 RPC 請求定位符如 Service Name、Method Name、Version 等,可以提到 Header 中,和具體的序列化協(xié)議解耦,以更好的被網(wǎng)絡(luò)基礎(chǔ)設(shè)施識別或用于流量管控。
擴(kuò)展性不夠好,欠缺協(xié)議升級方面的設(shè)計(jì),如 Header 頭中沒有預(yù)留的狀態(tài)標(biāo)識位,或者像 HTTP 有專為協(xié)議升級或協(xié)商設(shè)計(jì)的特殊 packet。
在 Java 版本的代碼實(shí)現(xiàn)上,不夠精簡和通用。如在鏈路傳輸中,存在一些語言綁定的內(nèi)容;消息體中存在冗余內(nèi)容,如 Service Name 在 Body 和 Attachments 中都存在。
HTTP/1
相比于直接構(gòu)建與 TPC 傳輸層的私有 RPC 協(xié)議,構(gòu)建于 HTTP 之上的遠(yuǎn)程調(diào)用解決方案會有更好的通用性,如WebServices 或 REST 架構(gòu),使用 HTTP + JSON 可以說是一個(gè)事實(shí)標(biāo)準(zhǔn)的解決方案。
之所有選擇構(gòu)建在 HTTP 之上,我認(rèn)為有兩個(gè)最大的優(yōu)勢:
HTTP 的語義和可擴(kuò)展性能很好的滿足 RPC 調(diào)用需求。
通用性,HTTP 協(xié)議幾乎被網(wǎng)絡(luò)上的所有設(shè)備所支持,具有很好的協(xié)議穿透性。
具體來說,HTTP/1 的優(yōu)勢和限制是:
典型的 Request – Response 模型,一個(gè)鏈路上一次只能有一個(gè)等待的 Request 請求
HTTP/1 支持 Keep-Alive 鏈接,避免了鏈接重復(fù)創(chuàng)建開銷
Human Readable Headers,使用更通用、更易于人類閱讀的頭部傳輸格式
無直接 Server Push 支持,需要使用 Polling Long-Polling 等變通模式
HTTP/2
HTTP/2 保留了 HTTP/1 的所有語義,在保持兼容的同時(shí),在通信模型和傳輸效率上做了很大的改進(jìn)。
支持單條鏈路上的 Multiplexing,相比于 Request - Response 獨(dú)占鏈路,基于 Frame 實(shí)現(xiàn)更高效利用鏈路
Request - Stream 語義,原生支持 Server Push 和 Stream 數(shù)據(jù)傳輸
Flow Control,單條 Stream 粒度的和整個(gè)鏈路粒度的流量控制
頭部壓縮 HPACK
Binary Frame
原生 TLS 支持
gRPC
上面提到了在 HTTP 及 TCP 協(xié)議之上構(gòu)建 RPC 協(xié)議各自的優(yōu)缺點(diǎn),相比于 Dubbo 構(gòu)建于 TPC 傳輸層之上,Google 選擇將 gRPC 直接定義在 HTTP/2 協(xié)議之上,關(guān)于 gRPC 的 基本介紹和 設(shè)計(jì)愿景請參考以上兩篇文章,我這里僅摘取 設(shè)計(jì)愿景 中幾個(gè)能反映 gRPC 設(shè)計(jì)目的特性來做簡單說明。
Coverage & Simplicity,協(xié)議設(shè)計(jì)和框架實(shí)現(xiàn)要足夠通用和簡單,能運(yùn)行在任何設(shè)備之上,甚至一些資源首先的如 IoT、Mobile 等設(shè)備。
Interoperability & Reach,要構(gòu)建在更通用的協(xié)議之上,協(xié)議本身要能被網(wǎng)絡(luò)上幾乎所有的基礎(chǔ)設(shè)施所支持。
General Purpose & Performant,要在場景和性能間做好平衡,首先協(xié)議本身要是適用于各種場景的,同時(shí)也要盡量有高的性能。
Payload Agnostic,協(xié)議上傳輸?shù)呢?fù)載要保持語言和平臺中立。
Streaming,要支持 Request - Response、Request - Stream、Bi-Steam 等通信模型。
Flow Control,協(xié)議自身具備流量感知和限制的能力。
Metadata Exchange,在 RPC 服務(wù)定義之外,提供額外附加數(shù)據(jù)傳輸?shù)哪芰Α?/p>
總的來說,在這樣的設(shè)計(jì)理念指導(dǎo)下,gRPC 最終被設(shè)計(jì)為一個(gè)跨語言、跨平臺的、通用的、高性能的、基于 HTTP/2 的 RPC 協(xié)議和框架。
Protobuf
Protocol buffers (Protobuf) 是 Google 推出的一個(gè)跨平臺、語言中立的結(jié)構(gòu)化數(shù)據(jù)描述和序列化的產(chǎn)品,它定義了一套結(jié)構(gòu)化數(shù)據(jù)定義的協(xié)議,同時(shí)也提供了相應(yīng)的 Compiler 工具,用來將語言中立的描述轉(zhuǎn)化為相應(yīng)語言的具體描述。
它的一些特性包括:
跨語言 跨平臺,語言中立的數(shù)據(jù)描述格式,默認(rèn)提供了生成多種語言的 Compiler 工具。
安全性,由于反序列化的范圍和輸出內(nèi)容格式都是 Compiler 在編譯時(shí)預(yù)生成的,因此繞過了類似 Java Deserialization Vulnarability 的問題。
二進(jìn)制 高性能
強(qiáng)類型
字段變更向后兼容
message?Person?{ ??????required?string?name?=?1; ??????required?int32?id?=?2; ??????optional?string?email?=?3; ??????enum?PhoneType?{ ????????MOBILE?=?0; ????????HOME?=?1; ????????WORK?=?2; ??????} ??????message?PhoneNumber?{ ????????required?string?number?=?1; ????????optional?PhoneType?type?=?2?[default?=?HOME]; ??????} ??????repeated?PhoneNumber?phone?=?4; ????}
除了結(jié)構(gòu)化數(shù)據(jù)描述之外,Protobuf 還支持定義 RPC 服務(wù),它允許我們定義一個(gè) .proto 的服務(wù)描述文件,進(jìn)而利用 Protobuf Compiler 工具生成特定語言和 RPC 框架的接口和 stub。后續(xù)將要具體講到的 gRPC + Protobuf、Dubbo-gRPC + Protobuf 以及 Dubbo + Protobuf 都是通過定制 Compiler 類實(shí)現(xiàn)的。
service?SearchService?{ ????rpc?Search?(SearchRequest)?returns?(SearchResponse); }
Dubbo 所做的支持
跨語言的服務(wù)開發(fā)涉及到多個(gè)方面,從服務(wù)定義、RPC 協(xié)議到序列化協(xié)議都要做到語言中立,同時(shí)還針對每種語言有對應(yīng)的 SDK 實(shí)現(xiàn)。雖然得益于社區(qū)的貢獻(xiàn),現(xiàn)在 Dubbo 在多語言 SDK 實(shí)現(xiàn)上逐步有了起色,已經(jīng)提供了包括 Java, Go, PHP, C#, Python, NodeJs, C 等版本的客戶端或全量實(shí)現(xiàn)版本,但在以上提到的跨語言友好型方面,以上三點(diǎn)還是有很多可改進(jìn)之處。
協(xié)議,上面我們已經(jīng)分析過 Dubbo 協(xié)議既有的缺點(diǎn),如果能在 HTTP/2 之上構(gòu)建應(yīng)用層協(xié)議,則無疑能避免這些弊端,同時(shí)最大可能的提高協(xié)議的穿透性,避免網(wǎng)關(guān)等協(xié)議轉(zhuǎn)換組件的存在,更有利于鏈路上的流量管控。考慮到 gRPC 是構(gòu)建在 HTTP/2 之上,并且已經(jīng)是云原生領(lǐng)域推薦的通信協(xié)議,Dubbo 在第一階段選擇了直接支持 gRPC 協(xié)議作為當(dāng)前的 HTTP/2 解決方案。我們也知道 gRPC 框架自身的弊端在于易用性不足以及服務(wù)治理能力欠缺(這也是目前絕大多數(shù)廠商不會直接裸用 gRPC 框架的原因),通過將其集成進(jìn) Dubbo 框架,用戶可以方便的使用 Dubbo 編程模型 + Dubbo 服務(wù)治理 + gRPC 協(xié)議通信的組合。
服務(wù)定義,當(dāng)前 Dubbo 的服務(wù)定義和具體的編程語言綁定,沒有提供一種語言中立的服務(wù)描述格式,比如 Java 就是定義 Interface 接口,到了其他語言又得重新以另外的格式定義一遍。因此 Dubbo 通過支持 Protobuf 實(shí)現(xiàn)了語言中立的服務(wù)定義。
序列化,Dubbo 當(dāng)前支持的序列化包括 Json、Hessian2、Kryo、FST、Java 等,而這其中支持跨語言的只有 Json、Hessian2,通用的 Json 有固有的性能問題,而 Hessian2 無論在效率還是多語言 SDK 方面都有所欠缺。為此,Dubbo 通過支持 Protobuf 序列化來提供更高效、易用的跨語言序列化方案。
示例
示例 1,使用 Dubbo 開發(fā) gRPC 服務(wù)
gRPC 是 Google 開源的構(gòu)建在 HTTP/2 之上的一個(gè) PRC 通信協(xié)議。Dubbo 依賴其靈活的協(xié)議擴(kuò)展機(jī)制,增加了對 gRPC (HTTP/2) 協(xié)議的支持。
目前的支持限定在 Dubbo Java 語言版本,后續(xù) Go 語言或其他語言版本將會以類似方式提供支持。下面,通過一個(gè)簡單的示例來演示如何在 Dubbo 中使用 gRPC 協(xié)議通信。
1. 定義服務(wù) IDL
首先,通過標(biāo)準(zhǔn)的 Protobuf 協(xié)議定義服務(wù)如下:
syntax?=?"proto3"; ???? ????option?java_multiple_files?=?true; ????option?java_package?=?"io.grpc.examples.helloworld"; ????option?java_outer_classname?=?"HelloWorldProto"; ????option?objc_class_prefix?=?"HLW"; ???? ????package?helloworld; ???? ????//?The?greeting?service?definition. ????service?Greeter?{ ??????//?Sends?a?greeting ??????rpc?SayHello?(HelloRequest)?returns?(HelloReply)?{} ????} ???? ????//?The?request?message?containing?the?user's?name. ????message?HelloRequest?{ ??????string?name?=?1; ????} ???? ????//?The?response?message?containing?the?greetings ????message?HelloReply?{ ??????string?message?=?1; ????}
在此,我們定義了一個(gè)只有一個(gè)方法 sayHello 的 Greeter 服務(wù),同時(shí)定義了方法的入?yún)⒑统鰠ⅲ?/p>
2. Protobuf Compiler 生成 Stub
定義 Maven Protobuf Compiler 插件工具。這里我們擴(kuò)展了 Protobuf 的 Compiler 工具,以用來生成 Dubbo 特有的 RPC stub,此當(dāng)前以 Maven 插件的形式發(fā)布。
??org.xolstice.maven.plugins ??protobuf-maven-plugin ??0.5.1 ?? ????com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}???? ???? ????dubbo-grpc-java ????org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier} ????build/generated/source/proto/main/java ????false ????grpc ?? ?? ???? ?????? ????????compile ????????compile-custom ?????? ???? ??
其中,
pluginArtifact 指定了 Dubbo 定制版本的 Java Protobuf Compiler 插件,通過這個(gè)插件來在編譯過程中生成 Dubbo 定制版本的 gRPC stub。
org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}
由于 protoc-gen-dubbo-java 支持 gRPC 和 Dubbo 兩種協(xié)議,可生成的 stub 類型,默認(rèn)值是 gRPC,關(guān)于 dubbo 協(xié)議的使用可參見 使用 Protobuf 開發(fā) Dubbo 服務(wù)。
grpc
2. 生成 Java Bean 和 Dubbo-gRPC stub
#?運(yùn)行以下?maven?命令 $?mvn?clean?compile
生成的 Stub 和消息類 如下:
重點(diǎn)關(guān)注 GreeterGrpc ,包含了所有 gRPC 標(biāo)準(zhǔn)的 stub 類/方法,同時(shí)增加了 Dubbo 特定的接口,之后 Provider 端的服務(wù)暴露和 Consumer 端的服務(wù)調(diào)用都將依賴這個(gè)接口。
/** *?Code?generated?for?Dubbo */ public?interface?IGreeter?{ ??default?public?io.grpc.examples.helloworld.HelloReply?????sayHello(io.grpc.examples.helloworld.HelloRequest?request)?{ ????throw?new?UnsupportedOperationException("No?need?to?override?this?method,?extend?XxxImplBase?and?override?all?methods?it?allows."); ??} ??default?public?com.google.common.util.concurrent.ListenableFuture?sayHelloAsync( ????io.grpc.examples.helloworld.HelloRequest?request)?{ ????throw?new?UnsupportedOperationException("No?need?to?override?this?method,?extend?XxxImplBase?and?override?all?methods?it?allows."); ??} ??public?void?sayHello(io.grpc.examples.helloworld.HelloRequest?request, ???????????????????????io.grpc.stub.StreamObserver?responseObserver); }
3. 業(yè)務(wù)邏輯開發(fā)
繼承 GreeterGrpc.GreeterImplBase (來自第 2 步),編寫業(yè)務(wù)邏輯,這點(diǎn)和原生 gRPC 是一致的。
package?org.apache.dubbo.samples.basic.impl; import?io.grpc.examples.helloworld.GreeterGrpc; import?io.grpc.examples.helloworld.HelloReply; import?io.grpc.examples.helloworld.HelloRequest; import?io.grpc.stub.StreamObserver; public?class?GrpcGreeterImpl?extends?GreeterGrpc.GreeterImplBase?{ ??@Override ??public?void?sayHello(HelloRequest?request,?StreamObserver?responseObserver)?????????{ ????System.out.println("Received?request?from?client."); ????System.out.println("Executing?thread?is?"?+?Thread.currentThread().getName()); ????HelloReply?reply?=?HelloReply.newBuilder() ??????.setMessage("Hello?"?+?????request.getName()).build(); ????responseObserver.onNext(reply); ????responseObserver.onCompleted(); ??} }
Provider 端暴露 Dubbo 服務(wù)
以 Spring XML 為例
引用 Dubbo 服務(wù)
示例1附:高級用法
一、異步調(diào)用
再來看一遍 protoc-gen-dubbo-java 生成的接口:
/** *?Code?generated?for?Dubbo */ public?interface?IGreeter?{ ??default?public?HelloReply?sayHello(HelloRequest?request)?{ ????//?...... ??} ??default?public?ListenableFuture?sayHelloAsync(HelloRequest?request)?{ ????//?...... ??} ??public?void?sayHello(HelloRequest?request,?StreamObserver?responseObserver); }
這里為 sayHello 方法生成了三種類型的重載方法,分別用于同步調(diào)用、異步調(diào)用和流式調(diào)用,如果消費(fèi)端要進(jìn)行異步調(diào)用,直接調(diào)用 sayHelloAsync() 即可:
public?static?void?main(String[]?args)?throws?IOException?{ ??//?... ??GreeterGrpc.IGreeter?greeter?=?(GreeterGrpc.IGreeter)?context.getBean("greeter"); ??ListenableFuture?future?=??? ????greeter.sayHAsyncello(HelloRequest.newBuilder().setName("world!").build()); ??//?... }
二、高級配置
由于當(dāng)前實(shí)現(xiàn)方式是直接集成了 gRPC-java SDK,因此很多配置還沒有和 Dubbo 側(cè)對齊,或者還沒有以 Dubbo 的配置形式開放,因此,為了提供最大的靈活性,我們直接把 gRPC-java 的配置接口暴露了出來。
絕大多數(shù)場景下,你可能并不會用到以下擴(kuò)展,因?yàn)樗鼈兏嗟氖菍?gRPC 協(xié)議的攔截或者 HTTP/2 層面的配置。同時(shí)使用這些擴(kuò)展點(diǎn)可能需要對 HTTP/2 或 gRPC 有基本的了解。
擴(kuò)展點(diǎn)
目前支持的擴(kuò)展點(diǎn)如下:
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.ServerTransportFilter
GrpcConfigurator 是最通用的擴(kuò)展點(diǎn),我們以此為例來說明一下,其基本定義如下:
public?interface?GrpcConfigurator?{ ??//?用來定制?gRPC?NettyServerBuilder ??default?NettyServerBuilder?configureServerBuilder(NettyServerBuilder?builder,?URL?url)?{ ????return?builder; ??} ??//?用來定制?gRPC?NettyChannelBuilder ??default?NettyChannelBuilder?configureChannelBuilder(NettyChannelBuilder?builder,?URL?url)?{ ????return?builder; ??} ??//?用來定制?gRPC?CallOptions,?定義某個(gè)服務(wù)在每次請求間傳遞數(shù)據(jù) ??default?CallOptions?configureCallOptions(CallOptions?options,?URL?url)?{ ????return?options; ??} }
以下是一個(gè)示例擴(kuò)展實(shí)現(xiàn):
public?class?MyGrpcConfigurator?implements?GrpcConfigurator?{ ??private?final?ExecutorService?executor?=?Executors ????.newFixedThreadPool(200,?new?NamedThreadFactory("Customized-grpc",?true)); ??@Override ??public?NettyServerBuilder?configureServerBuilder(NettyServerBuilder?builder,?URL?url)?{ ????return?builder.executor(executor); ??} ??@Override ??public?NettyChannelBuilder?configureChannelBuilder(NettyChannelBuilder?builder,?URL?url) ??{ ????return?builder.flowControlWindow(10); ??} ??@Override ??public?CallOptions?configureCallOptions(CallOptions?options,?URL?url)?{ ????return?options.withOption(CallOptions.Key.create("key"),?"value"); ??} }
配置為 Dubbo SPI,`resources/META-INF/services 增加配置文件
default=org.apache.dubbo.samples.basic.comtomize.MyGrpcConfigurator
指定 Provider 端線程池 默認(rèn)用的是 Dubbo 的線程池,有 fixed (默認(rèn))、cached、direct 等類型。以下演示了切換為業(yè)務(wù)自定義線程池。
private?final?ExecutorService?executor?=?Executors ?????????????.newFixedThreadPool(200,?new?NamedThreadFactory("Customized-grpc",?true)); public?NettyServerBuilder?configureServerBuilder(NettyServerBuilder?builder,?URL?url)? { ??return?builder.executor(executor); }
指定 Consumer 端限流值 設(shè)置 Consumer 限流值為 10
@Override public?NettyChannelBuilder?configureChannelBuilder(NettyChannelBuilder?builder,?URL?url) { ??return?builder.flowControlWindow(10); }
傳遞附加參數(shù) DemoService 服務(wù)調(diào)用傳遞 key
@Override public?CallOptions?configureCallOptions(CallOptions?options,?URL?url)?{ ??if?(url.getServiceInterface().equals("xxx.DemoService"))?{ ????return?options.withOption(CallOptions.Key.create("key"),?"value"); ??}?else?{ ????return?options; ??} }
三、雙向流式通信代碼中還提供了一個(gè)支持雙向流式通信的示例,同時(shí)提供了攔截流式調(diào)用的 Interceptor 擴(kuò)展示例實(shí)現(xiàn)。
*?MyClientStreamInterceptor,工作在?client?端,攔截發(fā)出的請求流和接收的響應(yīng)流 *?MyServerStreamInterceptor,工作在?server?端,攔截收到的請求流和發(fā)出的響應(yīng)流
四、TLS 配置
配置方式和 Dubbo 提供的通用的 TLS 支持一致,具體請參見 Dubbo 官方文檔
示例 2, 使用 Protobuf 開發(fā) Dubbo 服務(wù)
下面,我們以一個(gè)具體的示例來看一下基于 Protobuf 的 Dubbo 服務(wù)開發(fā)流程。
1. 定義服務(wù)
通過標(biāo)準(zhǔn) Protobuf 定義服務(wù)
syntax?=?"proto3"; ????option?java_multiple_files?=?true; ????option?java_package?=?"org.apache.dubbo.demo"; ????option?java_outer_classname?=?"DemoServiceProto"; ????option?objc_class_prefix?=?"DEMOSRV"; ????package?demoservice; ????//?The?demo?service?definition. ????service?DemoService?{ ??????rpc?SayHello?(HelloRequest)?returns?(HelloReply)?{} ????} ????//?The?request?message?containing?the?user's?name. ????message?HelloRequest?{ ??????string?name?=?1; ????} ????//?The?response?message?containing?the?greetings ????message?HelloReply?{ ??????string?message?=?1; ????}
這里定義了一個(gè) DemoService 服務(wù),服務(wù)只包含一個(gè) sayHello 方法,同時(shí)定義了方法的入?yún)⒑统鰠ⅰ?/p>
2. Compiler 編譯服務(wù)
引入 Protobuf Compiler Maven 插件,同時(shí)指定 protoc-gen-dubbo-java RPC 擴(kuò)展
??org.xolstice.maven.plugins ??protobuf-maven-plugin ??0.5.1 ?? ????com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}???? ???? ????dubbo-grpc-java ????org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier} ????build/generated/source/proto/main/java ????false ????dubbo ?? ?? ???? ?????? ????????compile ????????compile-custom ?????? ???? ??
注意,這里與 Dubbo 對 gRPC 支持部分的區(qū)別在于: dubbo
生成 RPC stub
#?運(yùn)行以下?maven?命令 $mvn?clean?compile
生成的 Java 類如下:
DemoServiceDubbo 為 Dubbo 定制的 stub
public?final?class?DemoServiceDubbo?{ ??private?static?final?AtomicBoolean?registered?=?new?AtomicBoolean(); ??private?static?Class ?init()?{ ????Class ?clazz?=?null; ????try?{ ??????clazz?=?Class.forName(DemoServiceDubbo.class.getName()); ??????if?(registered.compareAndSet(false,?true))?{ ????????org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller( ??????????org.apache.dubbo.demo.HelloRequest.getDefaultInstance()); ????????org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller( ??????????org.apache.dubbo.demo.HelloReply.getDefaultInstance()); ??????} ????}?catch?(ClassNotFoundException?e)?{ ??????//?ignore? ????} ????return?clazz; ??} ??private?DemoServiceDubbo()?{} ??public?static?final?String?SERVICE_NAME?=?"demoservice.DemoService"; ??/** ??????????*?Code?generated?for?Dubbo ??????????*/ ??public?interface?IDemoService?{ ????static?Class ?clazz?=?init(); ????org.apache.dubbo.demo.HelloReply?sayHello(org.apache.dubbo.demo.HelloRequest?request); ????java.util.concurrent.CompletableFuture?sayHelloAsync( ??????org.apache.dubbo.demo.HelloRequest?request); ??} }
最值得注意的是 IDemoService 接口,它會作為 Dubbo 服務(wù)定義基礎(chǔ)接口。
3. 開發(fā)業(yè)務(wù)邏輯
從這一步開始,所有開發(fā)流程就和直接定義 Java 接口一樣了。實(shí)現(xiàn)接口定義業(yè)務(wù)邏輯。
public?class?DemoServiceImpl?implements?DemoServiceDubbo.IDemoService?{ ??private?static?final?Logger?logger?=?LoggerFactory.getLogger(DemoServiceImpl.class); ??@Override ??public?HelloReply?sayHello(HelloRequest?request)?{ ????logger.info("Hello?"?+?request.getName()?+?",?request?from?consumer:?"?+?RpcContext.getContext().getRemoteAddress()); ????return?HelloReply.newBuilder() ??????.setMessage("Hello?"?+?request.getName()?+?",?response?from?provider:?" ??????????????????+?RpcContext.getContext().getLocalAddress()) ??????.build(); ??} ??@Override ??public?CompletableFuture?sayHelloAsync(HelloRequest?request)?{ ????return?CompletableFuture.completedFuture(sayHello(request)); ??} }
4. 配置 Provider
暴露 Dubbo 服務(wù)
5. 配置 Consumer
引用 Dubbo 服務(wù)
展望
RPC 協(xié)議是實(shí)現(xiàn)微服務(wù)體系互通的核心組件,通常采用不同的微服務(wù)通信框架則意味著綁定某一個(gè)特定的協(xié)議,如 Spring Cloud 基于 HTTP、gRPC 提供 gRPC over HTTP/2、Thrift Hessian 等都是自定義私有協(xié)議。
Dubbo 自身同樣提供了私有的 Dubbo 協(xié)議,這樣你也能基于 Dubbo 協(xié)議構(gòu)建微服務(wù)。但除了單一協(xié)議之外,和以上所有框架不同的,Dubbo 最大的優(yōu)勢在于它能同時(shí)支持多協(xié)議的暴露和消費(fèi),再配合 Dubbo 多注冊訂閱的模型,可以讓 Dubbo 成為橋接多種不同協(xié)議的微服務(wù)體系的開發(fā)框架,輕松的實(shí)現(xiàn)不同微服務(wù)體系的互調(diào)互通或技術(shù)棧遷移。
評論