W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
Triple 協(xié)議的格式和原理請參閱 RPC 通信協(xié)議
根據(jù) Triple 設(shè)計的目標(biāo),?Triple
?協(xié)議有以下優(yōu)勢:
當(dāng)前使用其他協(xié)議的 Dubbo 用戶,框架提供了兼容現(xiàn)有序列化方式的遷移能力,在不影響線上已有業(yè)務(wù)的前提下,遷移協(xié)議的成本幾乎為零。
需要新增對接 Grpc 服務(wù)的 Dubbo 用戶,可以直接使用 Triple 協(xié)議來實現(xiàn)打通,不需要單獨引入 grpc client 來完成,不僅能保留已有的 Dubbo 易用性,也能降低程序的復(fù)雜度和開發(fā)運維成本,不需要額外進行適配和開發(fā)即可接入現(xiàn)有生態(tài)。
對于需要網(wǎng)關(guān)接入的 Dubbo 用戶,Triple 協(xié)議提供了更加原生的方式,讓網(wǎng)關(guān)開發(fā)或者使用開源的 grpc 網(wǎng)關(guān)組件更加簡單。網(wǎng)關(guān)可以選擇不解析 payload ,在性能上也有很大提高。在使用 Dubbo 協(xié)議時,語言相關(guān)的序列化方式是網(wǎng)關(guān)的一個很大痛點,而傳統(tǒng)的 HTTP 轉(zhuǎn) Dubbo 的方式對于跨語言序列化幾乎是無能為力的。同時,由于 Triple 的協(xié)議元數(shù)據(jù)都存儲在請求頭中,網(wǎng)關(guān)可以輕松的實現(xiàn)定制需求,如路由和限流等功能。
Dubbo2 的用戶使用 dubbo 協(xié)議 + 自定義序列化,如 hessian2 完成遠程調(diào)用。
而 Grpc 的默認僅支持 Protobuf 序列化,對于 Java 語言中的多參數(shù)以及方法重載也無法支持。
Dubbo3的之初就有一條目標(biāo)是完美兼容 Dubbo2,所以為了 Dubbo2 能夠平滑升級, Dubbo 框架側(cè)做了很多工作來保證升級的無感,目前默認的序列化和 Dubbo2 保持一致為?hessian2
?。
所以,如果決定要升級到 Dubbo3 的 Triple 協(xié)議,只需要修改配置中的協(xié)議名稱為 tri (注意: 不是triple)即可。
接下來我們我們以一個使用 Dubbo2 協(xié)議的工程 來舉例,如何一步一步安全的升級。
dubbo
?協(xié)議啟動 ?provider
?和 ?consumer
?,并完成調(diào)用。dubbo
?和 ?tri
?協(xié)議 啟動?provider
?,以 ?dubbo
? 協(xié)議啟動 ?consumer
?,并完成調(diào)用。tri
?協(xié)議 啟動 ?provider
?和 ?consumer
?,并完成調(diào)用。public interface IWrapperGreeter { //... /** * 這是一個普通接口,沒有使用 pb 序列化 */ String sayHello(String request); }
public class IGreeter2Impl implements IWrapperGreeter { @Override public String sayHello(String request) { return "hello," + request; } }
為保證兼容性,我們先將部分 provider 升級到 ?dubbo3
?版本并使用 ?dubbo
?協(xié)議。
使用 dubbo
協(xié)議啟動一個 Provider
和 Consumer
,完成調(diào)用,輸出如下:
對于線上服務(wù)的升級,不可能一蹴而就同時完成 provider 和 consumer 升級, 需要按步操作,保證業(yè)務(wù)穩(wěn)定。 第二步, provider 提供雙協(xié)議的方式同時支持 dubbo + tri 兩種協(xié)議的客戶端。
結(jié)構(gòu)如圖所示:
按照推薦升級步驟,provider 已經(jīng)支持了tri協(xié)議,所以 dubbo3的 consumer 可以直接使用 tri 協(xié)議
使用dubbo
協(xié)議和triple
協(xié)議啟動Provider
和Consumer
,完成調(diào)用,輸出如下:
當(dāng)所有的 consuemr 都升級至支持 ?Triple
?協(xié)議的版本后,?provider
?可切換至僅使用 ?Triple
?協(xié)議啟動
結(jié)構(gòu)如圖所示:
Provider 和 Consumer 完成調(diào)用,輸出如下:
通過上面介紹的升級過程,我們可以很簡單的通過修改協(xié)議類型來完成升級??蚣苁窃趺磶臀覀冏龅竭@些的呢?
通過對 Triple 協(xié)議的介紹,我們知道Dubbo3的 Triple 的數(shù)據(jù)類型是 protobuf 對象,那為什么非 protobuf 的 java 對象也可以被正常傳輸呢。
這里 Dubbo3 使用了一個巧妙的設(shè)計,首先判斷參數(shù)類型是否為 protobuf 對象,如果不是。用一個 protobuf 對象將 request 和 response 進行 wrapper,這樣就屏蔽了其他各種序列化帶來的復(fù)雜度。在 wrapper 對象內(nèi)部聲明序列化類型,來支持序列化的擴展。
wrapper 的protobuf的 IDL如下:
syntax = "proto3"; package org.apache.dubbo.triple; message TripleRequestWrapper { // hessian4 // json string serializeType = 1; repeated bytes args = 2; repeated string argTypes = 3; } message TripleResponseWrapper { string serializeType = 1; bytes data = 2; string type = 3; }
對于請求,使用TripleRequestWrapper進行包裝,對于響應(yīng)使用TripleResponseWrapper進行包裝。
對于請求參數(shù),可以看到 args 被repeated修飾,這是因為需要支持 java 方法的多個參數(shù)。當(dāng)然,序列化只能是一種。序列化的實現(xiàn)沿用 Dubbo2 實現(xiàn)的 spi
建議新服務(wù)均使用該方式
對于 Dubbo3 和 Triple 來說,主推的是使用 protobuf 序列化,并且使用 proto 定義的 IDL 來生成相關(guān)接口定義。以 IDL 做為多語言中的通用接口約定,加上 Triple 與 Grpc 的天然互通性,可以輕松地實現(xiàn)跨語言交互,例如 Go 語言等。
將編寫好的 .proto 文件使用 dubbo-compiler 插件進行編譯并編寫實現(xiàn)類,完成方法調(diào)用:
從上面升級的例子我們可以知道,Triple 協(xié)議使用 protbuf 對象序列化后進行傳輸,所以對于本身就是 protobuf 對象的方法來說,沒有任何其他邏輯。
使用 protobuf 插件編譯后接口如下:
public interface PbGreeter { static final String JAVA_SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter"; static final String SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter"; static final boolean inited = PbGreeterDubbo.init(); org.apache.dubbo.sample.tri.GreeterReply greet(org.apache.dubbo.sample.tri.GreeterRequest request); default CompletableFuture<org.apache.dubbo.sample.tri.GreeterReply> greetAsync(org.apache.dubbo.sample.tri.GreeterRequest request){ return CompletableFuture.supplyAsync(() -> greet(request)); } void greetServerStream(org.apache.dubbo.sample.tri.GreeterRequest request, org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterReply> responseObserver); org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterRequest> greetStream(org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterReply> responseObserver); }
Stream 是 Dubbo3 新提供的一種調(diào)用類型,在以下場景時建議使用流的方式:
Stream 分為以下三種:
由于 java 語言的限制,BIDIRECTIONAL_STREAM 和 CLIENT_STREAM 的實現(xiàn)是一樣的。
在 Dubbo3 中,流式接口以 SteamObserver 聲明和使用,用戶可以通過使用和實現(xiàn)這個接口來發(fā)送和處理流的數(shù)據(jù)、異常和結(jié)束。
對于 Dubbo2 用戶來說,可能會對StreamObserver感到陌生,這是Dubbo3定義的一種流類型,Dubbo2 中并不存在 Stream 的類型,所以對于遷移場景沒有任何影響。
流的語義保證
public interface IWrapperGreeter { StreamObserver<String> sayHelloStream(StreamObserver<String> response); void sayHelloServerStream(String request, StreamObserver<String> response); }
Stream 方法的方法入?yún)⒑头祷刂凳菄栏窦s定的,為防止寫錯而導(dǎo)致問題,Dubbo3 框架側(cè)做了對參數(shù)的檢查, 如果出錯則會拋出異常。 對于 雙向流(BIDIRECTIONAL_STREAM), 需要注意參數(shù)中的 StreamObserver 是響應(yīng)流,返回參數(shù)中的 StreamObserver 為請求流。
public class WrapGreeterImpl implements WrapGreeter { //... @Override public StreamObserver<String> sayHelloStream(StreamObserver<String> response) { return new StreamObserver<String>() { @Override public void onNext(String data) { System.out.println(data); response.onNext("hello,"+data); } @Override public void onError(Throwable throwable) { throwable.printStackTrace(); } @Override public void onCompleted() { System.out.println("onCompleted"); response.onCompleted(); } }; } @Override public void sayHelloServerStream(String request, StreamObserver<String> response) { for (int i = 0; i < 10; i++) { response.onNext("hello," + request); } response.onCompleted(); } }
delegate.sayHelloServerStream("server stream", new StreamObserver<String>() { @Override public void onNext(String data) { System.out.println(data); } @Override public void onError(Throwable throwable) { throwable.printStackTrace(); } @Override public void onCompleted() { System.out.println("onCompleted"); } }); StreamObserver<String> request = delegate.sayHelloStream(new StreamObserver<String>() { @Override public void onNext(String data) { System.out.println(data); } @Override public void onError(Throwable throwable) { throwable.printStackTrace(); } @Override public void onCompleted() { System.out.println("onCompleted"); } }); for (int i = 0; i < n; i++) { request.onNext("stream request" + i); } request.onCompleted();
對于 Protobuf 序列化方式,推薦編寫 IDL 使用 compiler 插件進行編譯生成。生成的代碼大致如下:
public interface PbGreeter { static final String JAVA_SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter"; static final String SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter"; static final boolean inited = PbGreeterDubbo.init(); //... void greetServerStream(org.apache.dubbo.sample.tri.GreeterRequest request, org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterReply> responseObserver); org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterRequest> greetStream(org.apache.dubbo.common.stream.StreamObserver<org.apache.dubbo.sample.tri.GreeterReply> responseObserver); }
Triple協(xié)議的流模式是怎么支持的呢?
關(guān)于 Triple 協(xié)議的應(yīng)用級服務(wù)注冊和發(fā)現(xiàn)和其他語言是一致的,可以通過下列內(nèi)容了解更多。
通過對于協(xié)議的介紹,我們知道 Triple 協(xié)議是基于 HTTP2 并兼容 GRPC。為了保證和驗證與GRPC互通能力,Dubbo3 也編寫了各種從場景下的測試。詳細的可以通過這里 了解更多。
用過 Grpc 的同學(xué)應(yīng)該對 Stub 都不陌生。 Grpc 使用 compiler 將編寫的 proto 文件編譯為相關(guān)的 protobuf 對象和相關(guān) rpc 接口。默認的會同時生成幾種不同的 stub
stub 用一種統(tǒng)一的使用方式幫我們屏蔽了不同調(diào)用方式的細節(jié)。不過目前 Dubbo3 暫時只支持傳統(tǒng)定義接口并進行調(diào)用的使用方式。
在不久的未來,Triple 也將實現(xiàn)各種常用的 Stub,讓用戶寫一份proto文件,通過 comipler 可以在任意場景方便的使用,請拭目以待。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: