欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Triple協(xié)議支持Java異?;貍髟O計實現(xiàn)詳解

 更新時間:2022年12月30日 15:57:25   作者:陳景明  
這篇文章主要為大家介紹了Triple協(xié)議支持Java異常回傳設計實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

背景

在一些業(yè)務場景, 往往需要自定義異常來滿足特定的業(yè)務, 主流用法是在catch里拋出異常, 例如:

public void deal() {
  try{
   //doSomething   
   ...
  } catch(IGreeterException e) {
      ...
      throw e;
  }   
}

或者通過ExceptionBuilder,把相關(guān)的異常對象返回給consumer:

provider.send(new ExceptionBuilders.IGreeterExceptionBuilder()
    .setDescription('異常描述信息'); 

在拋出異常后, 通過捕獲和instanceof來判斷特定的異常, 然后做相應的業(yè)務處理,例如:

try {
    greeterProxy.echo(REQUEST_MSG);
} catch (IGreeterException e) {
    //做相應的處理
    ...
}

在 Dubbo 2.x 版本,可以通過上述方法來捕獲 Provider 端的異常。 而隨著云原生時代的到來, Dubbo 也開啟了 3.0 的里程碑。

Dubbo 3.0 的一個很重要的目標就是全面擁抱云原生, 在 3.0 的許多特性中,很重要的一個改動就是支持新的一代Rpc協(xié)議Triple。

Triple 協(xié)議基于 HTTP 2.0 進行構(gòu)建,對網(wǎng)關(guān)的穿透性強,兼容 gRPC, 提供 Request Response、Request Streaming、Response Streaming、 Bi-directional Streaming 等通信模型; 從 Triple 協(xié)議開始,Dubbo 還支持基于 IDL 的服務定義。

采用 Triple 協(xié)議的用戶可以在 provider 端生成用戶定義的異常信息, 記錄異常產(chǎn)生的堆棧,triple 協(xié)議可保證將用戶在客戶端獲取到異常的message。

Triple 的回傳異常會在 AbstractInvokerwaitForResultIfSync 中把異常信息堆棧統(tǒng)一封裝成 RpcException, 所有來自 Provider 端的異常都會被封裝成 RpcException 類型并拋出, 這會導致用戶無法根據(jù)特定的異常類型捕獲來自 Provider 的異常, 只能通過捕獲 RpcException 異常來返回信息, 且 Provider 攜帶的異常 message 也無法回傳,只能獲取打印的堆棧信息:

    try {
        greeterProxy.echo(REQUEST_MSG);
    } catch (RpcException e) {
        e.printStackTrace();
    }

自定義異常信息在社區(qū)中的呼聲也比較高, 因此本次改動將支持自定義異常的功能, 使得服務端能拋出自定義異常后被客戶端捕獲到。

Dubbo異常處理簡介

我們從Consumer的角度看一下一次Triple協(xié)議 Unary請求的大致流程:

Dubbo Consumer 從 Spring 容器中獲取 bean 時獲取到的是一個代理接口, 在調(diào)用接口的方法時會通過代理類遠程調(diào)用接口并返回結(jié)果。

Dubbo提供的代理工廠類是 ProxyFactory,通過 SPI 機制默認實現(xiàn)的是 JavassistProxyFactory, JavassistProxyFactory 創(chuàng)建了一個繼承自 AbstractProxyInvoker 類的匿名對象, 并重寫了抽象方法 doInvoke。 重寫后的 doInvoke 只是將調(diào)用請求轉(zhuǎn)發(fā)給了 Wrapper 類的 invokeMethod 方法, 并生成 invokeMethod 方法代碼和其他一些方法代碼。

代碼生成完畢后,通過 Javassist 生成 Class 對象, 最后再通過反射創(chuàng)建 Wrapper 實例,隨后通過 InvokerInvocationHandler -> InvocationUtil -> AbstractInvoker -> 具體實現(xiàn)類發(fā)送請求到Provider端。

Provider 進行相應的業(yè)務處理后返回相應的結(jié)果給 Consumer 端,來自 Provider 端的結(jié)果會被封裝成 AsyncResult ,在 AbstractInvoker 的具體實現(xiàn)類里, 接受到來自 Provider 的響應之后會調(diào)用 appResponserecreate 方法,若 appResponse 里包含異常, 則會拋出給用戶,大體流程如下:

上述的異常處理相關(guān)環(huán)節(jié)是在 Consumer 端,在 Provider 端則是由 org.apache.dubbo.rpc.filter.ExceptionFilter 進行處理, 它是一系列責任鏈 Filter 中的一環(huán),專門用來處理異常。

Dubbo 在 Provider 端的異常會在封裝進 appResponse 中。下面的流程圖揭示了 ExceptionFilter 源碼的異常處理流程:

而當 appResponse 回到了 Consumer 端,會在 InvocationUtil 里調(diào)用 AppResponserecreate 方法拋出異常, 最終可以在 Consumer 端捕獲:

public Object recreate() throws Throwable {
    if (exception != null) {
    try {
        Object stackTrace = exception.getStackTrace();
        if (stackTrace == null) {
            exception.setStackTrace(new StackTraceElement[0]);
        }
    } catch (Exception e) {
        // ignore
    }
    throw exception;
}
return result;
}

Triple 通信原理

在上一節(jié)中,我們已經(jīng)介紹了 Dubbo 在 Consumer 端大致發(fā)送數(shù)據(jù)的流程, 可以看到最終依靠的是 AbstractInvoker 的實現(xiàn)類來發(fā)送數(shù)據(jù)。 在 Triple 協(xié)議中,AbstractInvoker 的具體實現(xiàn)類是 TripleInvokerTripleInvoker 在發(fā)送前會啟動監(jiān)聽器,監(jiān)聽來自 Provider 端的響應結(jié)果, 并調(diào)用 ClientCallToObserverAdapteronNext 方法發(fā)送消息, 最終會在底層封裝成 Netty 請求發(fā)送數(shù)據(jù)。

在正式的請求發(fā)起前,TripleServer 會注冊 TripleHttp2FrameServerHandler, 它繼承自 Netty 的 ChannelDuplexHandler, 其作用是會在 channelRead 方法中不斷讀取 Header 和 Data 信息并解析, 經(jīng)過層層調(diào)用, 會在 AbstractServerCallonMessage 方法里把來自 consumer 的信息流進行反序列化, 并最終由交由 ServerCallToObserverAdapterinvoke 方法進行處理。

invoke 方法中,根據(jù) consumer 請求的數(shù)據(jù)調(diào)用服務端相應的方法,并異步等待結(jié)果;' 若服務端拋出異常,則調(diào)用 onError 方法進行處理, 否則,調(diào)用 onReturn 方法返回正常的結(jié)果,大致代碼邏輯如下:

public void invoke() {
    ...
    try {
        //調(diào)用invoke方法請求服務
        final Result response = invoker.invoke(invocation);
        //異步等待結(jié)果
        response.whenCompleteWithContext((r, t) -> {
            //若異常不為空
            if (t != null) {
                //調(diào)用方法過程出現(xiàn)異常,調(diào)用onError方法處理
                responseObserver.onError(t);
                return;
            }
            if (response.hasException()) {
                //調(diào)用onReturn方法處理業(yè)務異常
                onReturn(response.getException());
                return;
            }
            ...
            //正常返回結(jié)果
            onReturn(r.getValue());
        });
    } 
    ...
}

大體流程如下:

實現(xiàn)版本

了解了上述原理,我們就可以進行相應的改造了, 能讓 consumer 端捕獲異常的關(guān)鍵在于把異常對象以及異常信息序列化后再發(fā)送給consumer端。 常見的序列化協(xié)議很多,例如 Dubbo/HSF 默認的 hessian2 序列化; 還有使用廣泛的 JSON 序列化;以及 gRPC 原生支持的 protobuf(PB) 序列化等等。 Triple協(xié)議因為兼容grpc的原因,默認采用 Protobuf 進行序列化。 上述提到的這三種典型的序列化方案作用類似,但在實現(xiàn)和開發(fā)中略有不同。 PB 不可由序列化后的字節(jié)流直接生成內(nèi)存對象, 而 Hessian 和 JSON 都是可以的。后兩者反序列化的過程不依賴“二方包”, 其序列化和反序列化的代碼由 proto 文件相同,只要客戶端和服務端用相同的 proto 文件進行通信, 就可以構(gòu)造出通信雙方可解析的結(jié)構(gòu)。

單一的 protobuf 無法序列化異常信息, 因此我們采用 Wrapper + PB 的形式進行序列化異常信息, 抽象出一個 TripleExceptionWrapperUtils 用于序列化異常, 并在 trailer 中采用 TripleExceptionWrapperUtils 序列化異常,大致代碼流程如下:

上面的實現(xiàn)方案看似非常合理,已經(jīng)能把 Provider 端的異常對象和信息回傳, 并在 Consumer 端進行捕獲。但仔細想想還是有問題的: 通常在 HTTP2 為基礎的通信協(xié)議里會對 header 大小做一定的限制, 太大的header size 會導致性能退化嚴重,為了保證性能, 往往以 HTTP2 為基礎的協(xié)議在建立連接的時候是要協(xié)商最大 header size 的, 超過后會發(fā)送失敗。對于 Triple 協(xié)議來說,在設計之初就是基于 HTTP 2.0, 能無縫兼容 Grpc,而 Grpc header 頭部只有 8KB 大小, 異常對象大小可能超過限制,從而丟失異常信息; 且多一個 header 攜帶序列化的異常信息意味著用戶能加的 header 數(shù)量會減少, 擠占了其他 header 所能占用的空間。

經(jīng)過討論,考慮將異常信息放置在 Body,將序列化后的異常從 trailer 挪至 body, 采用 TripleWrapper + protobuf 進行序列化,把相關(guān)的異常信息序列化后回傳。 社區(qū)圍繞這個問題進行了一系列的爭論,讀者也可嘗試先思考一下:

1.在 body 中攜帶回傳的異常信息,其對應HTTP header狀態(tài)碼該設置為多少?

2.基于 http2 構(gòu)建的協(xié)議,按照主流的 grpc 實現(xiàn)方案,相關(guān)的錯誤信息放在 trailer,理論上不存在body,上層協(xié)議也需要保持語義一致性,若此時在payload回傳異常對象,且grpc并沒有支持在Body回傳序列化對象的功能, 會不會破壞Http和grpc協(xié)議的語義?從這個角度出發(fā),異常信息更應該放在trailer里。

3.作為開源社區(qū),不能一味滿足用戶的需求,非標準化的用法注定是會被淘汰的,應該盡量避免更改 Protobuf的語義,是否在Wrapper層去支持序列化異常就能滿足需求?

首先回答第二、三個問題:HTTP 協(xié)議并沒有約定在狀態(tài)碼非 2xx 的時候不能返回 body,返回之后是否讀取取決于用戶。grpc 采用protobuf進行序列化,所以無法返回 exception;且try catch機制為java獨有,其他語言并沒有對應的需求,但Grpc暫時不支持的功能并一定是unimplemented,Dubbo的設計目標之一是希望能和主流協(xié)議甚至架構(gòu)進行對齊,但對于用戶合理的需求也希望能進行一定程度的修改。且從throw本身的語義出發(fā),throw 的數(shù)據(jù)不只是一個 error message,序列化的異常信息帶有業(yè)務屬性,根據(jù)這個角度,更不應該采用類似trailer的設計。至于單一的Wrapper層,也沒辦法和grpc進行互通。至于Http header狀態(tài)碼設置為200,因為其返回的異常信息已經(jīng)帶有一定的業(yè)務屬性,不再是單純的error,這個設計也與grpc保持一致,未來考慮網(wǎng)關(guān)采集可以增加新的triple-status。

更改后的版本只需在異常不為空時返回相關(guān)的異常信息,采用 TripleWrapper + Protobuf 進行序列化異常信息,并在consumer端進行解析和反序列化,大體流程如下:

總結(jié)

通過對 Dubbo 3.0 新增自定義異常的版本迭代中可以看出,盡管只能新增一個小小的特性,流程下并不復雜,但由于要考慮互通、兼容和協(xié)議的設計理念,因此思考和討論的時間可能比寫代碼的時間更多。

以上就是Triple協(xié)議支持Java異常回傳設計實現(xiàn)詳解的詳細內(nèi)容,更多關(guān)于Java異?;貍鱐riple協(xié)議的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot WebSocket實時監(jiān)控異常的詳細流程

    SpringBoot WebSocket實時監(jiān)控異常的詳細流程

    最近做了一個需求,消防的設備巡檢,如果巡檢發(fā)現(xiàn)異常,通過手機端提交,后臺的實時監(jiān)控頁面實時獲取到該設備的信息及位置,然后安排員工去處理。這篇文章主要介紹了SpringBoot WebSocket實時監(jiān)控異常的全過程,感興趣的朋友一起看看吧
    2021-10-10
  • 詳解Java 中 RMI 的使用

    詳解Java 中 RMI 的使用

    這篇文章主要介紹了Java 中 RMI 的使用,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-05-05
  • Spring?MVC數(shù)據(jù)響應處理詳解

    Spring?MVC數(shù)據(jù)響應處理詳解

    這篇文章主要給大家介紹了關(guān)于Spring?MVC數(shù)據(jù)響應處理的相關(guān)資料,本教程詳細的講解SpringMVC框架的使用,非常詳細的案例講解,一步一步帶你走入springmvc框架的核心,需要的朋友可以參考下
    2022-05-05
  • Java中如何用Stream分組并求各組數(shù)量

    Java中如何用Stream分組并求各組數(shù)量

    這篇文章主要給大家介紹了關(guān)于Java中如何用Stream分組并求各組數(shù)量的相關(guān)資料,文中通過實例代碼,對大家學習或者Java具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • Spring 實現(xiàn)給Bean屬性注入null值

    Spring 實現(xiàn)給Bean屬性注入null值

    這篇文章主要介紹了Spring 實現(xiàn)給Bean屬性注入null值的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java Swing組件實現(xiàn)進度監(jiān)視功能示例

    Java Swing組件實現(xiàn)進度監(jiān)視功能示例

    這篇文章主要介紹了Java Swing組件實現(xiàn)進度監(jiān)視功能,結(jié)合完整實例形式詳細分析了Java基于Swing組件實現(xiàn)進度條顯示功能的具體操作技巧與相關(guān)注意事項,需要的朋友可以參考下
    2018-02-02
  • Java @RequestMapping注解功能使用詳解

    Java @RequestMapping注解功能使用詳解

    通過@RequestMapping注解可以定義不同的處理器映射規(guī)則,下面這篇文章主要給大家介紹了關(guān)于SpringMVC中@RequestMapping注解用法的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-11-11
  • 基于Protobuf動態(tài)解析在Java中的應用 包含例子程序

    基于Protobuf動態(tài)解析在Java中的應用 包含例子程序

    下面小編就為大家?guī)硪黄赑rotobuf動態(tài)解析在Java中的應用 包含例子程序。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • 輕松掌握java裝飾者模式

    輕松掌握java裝飾者模式

    這篇文章主要幫助大家輕松掌握java裝飾者模式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Java8中forEach語句循環(huán)一個List和Map

    Java8中forEach語句循環(huán)一個List和Map

    這篇文章主要給大家介紹了關(guān)于Java8中forEach語句循環(huán)一個List和Map的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-02-02

最新評論