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

Triple協(xié)議支持Java異常回傳設(shè)計(jì)實(shí)現(xiàn)詳解

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

背景

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

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

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

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

在拋出異常后, 通過(guò)捕獲和instanceof來(lái)判斷特定的異常, 然后做相應(yīng)的業(yè)務(wù)處理,例如:

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

在 Dubbo 2.x 版本,可以通過(guò)上述方法來(lái)捕獲 Provider 端的異常。 而隨著云原生時(shí)代的到來(lái), Dubbo 也開(kāi)啟了 3.0 的里程碑。

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

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

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

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

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

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

Dubbo異常處理簡(jiǎn)介

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

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

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

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

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

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

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

而當(dāng) appResponse 回到了 Consumer 端,會(huì)在 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 的實(shí)現(xiàn)類來(lái)發(fā)送數(shù)據(jù)。 在 Triple 協(xié)議中,AbstractInvoker 的具體實(shí)現(xiàn)類是 TripleInvoker , TripleInvoker 在發(fā)送前會(huì)啟動(dòng)監(jiān)聽(tīng)器,監(jiān)聽(tīng)來(lái)自 Provider 端的響應(yīng)結(jié)果, 并調(diào)用 ClientCallToObserverAdapteronNext 方法發(fā)送消息, 最終會(huì)在底層封裝成 Netty 請(qǐng)求發(fā)送數(shù)據(jù)。

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

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

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

大體流程如下:

實(shí)現(xiàn)版本

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

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

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

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

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

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

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

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

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

總結(jié)

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

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

相關(guān)文章

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

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

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

    詳解Java 中 RMI 的使用

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

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

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

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

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

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

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

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

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

    Java @RequestMapping注解功能使用詳解

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

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

    下面小編就為大家?guī)?lái)一篇基于Protobuf動(dòng)態(tài)解析在Java中的應(yīng)用 包含例子程序。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • 輕松掌握java裝飾者模式

    輕松掌握java裝飾者模式

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

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

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

最新評(píng)論