<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          什么是服務(wù)降級?DUBBO服務(wù)降級策略不能降級哪類異常?

          共 14177字,需瀏覽 29分鐘

           ·

          2021-01-24 03:47


          JAVA前線?


          歡迎大家關(guān)注公眾號「JAVA前線」查看更多精彩分享,主要包括源碼分析、實際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時也非常歡迎大家加我微信「java_front」一起交流學習


          1 服務(wù)雪崩

          在分析服務(wù)降級之前,我們首先談一談什么是服務(wù)雪崩。現(xiàn)在我們假設(shè)存在A、B、C、D四個系統(tǒng),系統(tǒng)間存在如下調(diào)用鏈路:


          在正常情況下系統(tǒng)之間調(diào)用快速且正常,系統(tǒng)運行平穩(wěn)。但是此時用戶訪問系統(tǒng)A的流量激增,這些流量在瞬間透傳到B、C、D三個系統(tǒng)。B、C系統(tǒng)服務(wù)器節(jié)點較多抗住了這些流量,但是D系統(tǒng)服務(wù)器節(jié)點較少,沒有抗住這些流量,導(dǎo)致D系統(tǒng)的資源逐漸耗盡,只能提供慢服務(wù),最終結(jié)果是響應(yīng)用戶時延很長。


          此時用戶發(fā)現(xiàn)響應(yīng)很慢,以為是自己網(wǎng)絡(luò)不好會反復(fù)重試,那么成倍的流量會打到系統(tǒng)中,導(dǎo)致上游系統(tǒng)資源也逐漸耗盡了,整個訪問鏈路都最終都不可用。


          以上介紹了服務(wù)雪崩場景,我們發(fā)現(xiàn)在鏈路中一個節(jié)點出現(xiàn)問題,導(dǎo)致整個鏈路最終都不可用了,這是不可以接受的。


          2 非線性

          我們再從另一個概念來理解服務(wù)雪崩:非線性。這個概念在我們生活中無處不在。

          你要趕早上8點鐘的火車,如果6:30出發(fā)可以在7:00到達車站,于是你得到一個結(jié)論:只要30分鐘就可以到達車站。

          你早上想睡晚一點預(yù)計7:10出發(fā),想著7:40可以到達車站。但是最可能的結(jié)果是你將錯過這趟火車。因為正好遇上早高峰,堵車導(dǎo)致你至少需要花費1個小時才能到達車站。

          一個小雪球的重量是100克,打雪仗時你被砸中100次,這對你不會造成任何影響。

          但是如果你被10公斤的雪球砸中1次,這可能會對你造成嚴重的傷害。

          這就是非線性。事物不是簡單疊加關(guān)系,當達到某個臨界值時會造成一種完全截然不同的結(jié)果。

          我們來分析一個互聯(lián)網(wǎng)的秒殺場景。假設(shè)你設(shè)計的秒殺系統(tǒng)當每秒30個人訪問時,響應(yīng)時間是10毫秒。即從用戶點擊按鈕至得到結(jié)果這個過程,只花費了10毫秒。這個時間的流逝基本上察覺不到,性能是不錯的。你感覺很好繼續(xù)設(shè)計:

          每秒30個訪問量響應(yīng)時間10毫秒

          每秒300個訪問量響應(yīng)時間100毫秒

          每秒3000個訪問量響應(yīng)時間1000毫秒

          如果你按照這個思路去做系統(tǒng)設(shè)計,將會發(fā)生重大的錯誤。因為當每秒3000個訪問量發(fā)生時,系統(tǒng)的響應(yīng)時間可能不是1000毫秒,而可能直接導(dǎo)致系統(tǒng)崩潰,無法再處理任何的請求。最常見的場景就是當緩存系統(tǒng)失效時,導(dǎo)致的系統(tǒng)雪崩:

          (1) 當耗時低的緩存層出現(xiàn)故障時,流量直接打在了耗時高的數(shù)據(jù)庫層,用戶的等待時長就會增加

          (2) 等待時長的增加導(dǎo)致用戶更加頻繁去訪問,更多的流量會打在數(shù)據(jù)庫層

          (3) 這導(dǎo)致用戶的等待時長進一步增加,再次導(dǎo)致更頻繁的訪問

          (4) 當訪問量達到一個極限值時,造成系統(tǒng)崩潰,無法再處理任何請求

          流量和響應(yīng)時間絕不是簡單的疊加關(guān)系,當?shù)竭_某個臨界值時,技術(shù)系統(tǒng)將直接崩潰。


          3 服務(wù)雪崩應(yīng)對方案

          保證系統(tǒng)的穩(wěn)定性和高可用性,我們需要采取一些高可用策略,目的是構(gòu)建一個穩(wěn)定的高可用工程系統(tǒng),我們一般采用如下方案。


          3.1 冗余 + 自動故障轉(zhuǎn)移

          最基本的冗余策略就是主從模式。原理是準備兩臺機器,部署了同一份代碼,在功能層面是相同的,都可以對外提供相同的服務(wù)。

          一臺機器啟動提供服務(wù),這就是主服務(wù)器。另一臺機器啟動在一旁待命,不提供服務(wù),隨時監(jiān)聽主服務(wù)器的狀態(tài),這就是從服務(wù)器。當發(fā)現(xiàn)主服務(wù)器出現(xiàn)故障時,從服務(wù)器立刻替換主服務(wù)器,繼續(xù)為用戶提供服務(wù)。

          自動故障轉(zhuǎn)移策略是指當主系統(tǒng)發(fā)生異常時,應(yīng)該可以自動探測到異常,并自動切換為備用系統(tǒng)。不應(yīng)該只依靠人工去切換成,否則故障處理時間會顯著增加。


          3.2 降級策略

          所謂降級策略,就是當系統(tǒng)遇到無法承受的壓力時,選擇暫時關(guān)閉一些非關(guān)鍵的功能,或者延時提供一些功能,把此刻所有的資源都提供給現(xiàn)在最關(guān)鍵的服務(wù)。

          在秒殺場景中下訂單就是最核心最關(guān)鍵的功能。當系統(tǒng)壓力將要到達臨界值時,可以暫時先關(guān)閉一些非核心功能如查詢功能。

          當秒殺活動結(jié)束后,再將暫時關(guān)閉的功能開啟。這樣既保證了秒殺活動的順利進行,也保護了系統(tǒng)沒有崩潰。

          還有一種降級策略,當系統(tǒng)依賴的下游服務(wù)出現(xiàn)錯誤,甚至已經(jīng)完全不可用了,那么此時就不能再調(diào)用這個下游服務(wù)了,否則可能導(dǎo)致雪崩。所以直接返回兜底方案,把下游服務(wù)直接降級。

          這里比較兩個概念:服務(wù)降級與服務(wù)熔斷,因為這兩個概念比較相似。我認為服務(wù)熔斷是服務(wù)降級的一個方法,而服務(wù)降級還有很多其它方法,例如開關(guān)降級、流量降級等等。


          3.3 延時策略

          用戶下訂單成功后就需要進行支付。假設(shè)秒殺系統(tǒng)下訂單每秒訪問量是3000,我們來思考一個問題,有沒有必要將每秒3000次訪問量的壓力傳遞給支付服務(wù)器?

          答案是沒有必要。因為用戶秒殺成功后可以稍晚付款,比如可以跳轉(zhuǎn)到一個支付頁面,提示用戶只要在10分鐘內(nèi)支付完成即可。

          這樣每秒3000次訪問量就被分攤至幾分鐘,有效保護了系統(tǒng)。技術(shù)架構(gòu)還可以使用消息隊列做緩沖,讓支付服務(wù)按照自己的能力去處理業(yè)務(wù)。


          3.4 隔離策略

          物理隔離:應(yīng)用分別部署在不同物理機、不同機房,資源不會互相影響。

          線程隔離:不同類型的請求進行分類,交給不同的線程池處理,當一類請求出現(xiàn)高耗時和異常,不影響另一類請求訪問。


          4 服務(wù)降級

          本文我們重點結(jié)合Dubbo框架談一談服務(wù)降級。現(xiàn)在我們有服務(wù)提供者提供如下服務(wù):

          public?interface?HelloService?{
          ????public?String?sayHello(String?name)?throws?Exception;
          }

          public?class?HelloServiceImpl?implements?HelloService?{
          ????public?String?sayHello(String?name)?throws?Exception?{
          ????????String?result?=?"hello["?+?name?+?"]";
          ????????return?result;
          ????}
          }

          配置文件聲明服務(wù)接口:

          <dubbo:service?interface="com.java.front.demo.provider.HelloService"?ref="helloService"?/>

          4.1 降級策略配置

          Dubbo框架是自帶服務(wù)降級策略的,提供了三種常用的降級策略,我們看一看如何進行配置。

          (1) 強制降級策略

          <dubbo:reference?id="helloService"?mock="force:return?1"?interface="com.java.front.demo.provider.HelloService"?/>

          (2) 異常降級策略

          <dubbo:reference?id="helloService"?mock="throw?com.java.front.BizException"?interface="com.java.front.dubbo.demo.provider.HelloService"?/>

          (3) 自定義降級策略

          package?com.java.front.dubbo.demo.consumer;
          import?com.java.front.demo.provider.HelloService;

          public?class?HelloServiceMock?implements?HelloService?{

          ????@Override
          ????public?String?sayHello(String?name)?throws?Exception?{
          ????????return?"mock";
          ????}
          }

          配置指定自定義降級策略:

          <dubbo:reference?id="helloService"?mock="com.java.front.dubbo.demo.consumer.HelloServiceMock"?interface="com.java.front.demo.provider.HelloService"?/>

          4.2 源碼分析

          public?class?MockClusterInvoker<T>?implements?Invoker<T>?{

          ????@Override
          ????public?Result?invoke(Invocation?invocation)?throws?RpcException?{
          ????????Result?result?=?null;

          ????????//?檢查是否有mock屬性
          ????????String?value?=?directory.getUrl().getMethodParameter(invocation.getMethodName(),?Constants.MOCK_KEY,?Boolean.FALSE.toString()).trim();

          ????????//?沒有mock屬性直接執(zhí)行消費邏輯
          ????????if?(value.length()?==?0?||?value.equalsIgnoreCase("false"))?{

          ????????????//?服務(wù)消費默認執(zhí)行FailoverClusterInvoker
          ????????????result?=?this.invoker.invoke(invocation);
          ????????}

          ????????//?不執(zhí)行消費邏輯直接返回
          ????????else?if?(value.startsWith("force"))?{
          ????????????if?(logger.isWarnEnabled())?{
          ????????????????logger.warn("force-mock:?"?+?invocation.getMethodName()?+?"?force-mock?enabled?,?url?:?"?+?directory.getUrl());
          ????????????}
          ????????????//?直接執(zhí)行mock邏輯
          ????????????result?=?doMockInvoke(invocation,?null);
          ????????}?else?{
          ????????????try?{
          ????????????????//?服務(wù)消費默認執(zhí)行FailoverClusterInvoker
          ????????????????result?=?this.invoker.invoke(invocation);
          ????????????}?catch?(RpcException?e)?{
          ????????????????if?(e.isBiz())?{
          ????????????????????throw?e;
          ????????????????}
          ????????????????if?(logger.isWarnEnabled())?{
          ????????????????????logger.warn("fail-mock:?"?+?invocation.getMethodName()?+?"?fail-mock?enabled?,?url?:?"?+?directory.getUrl(),?e);
          ????????????????}
          ????????????????//?服務(wù)消費失敗執(zhí)行mock邏輯
          ????????????????result?=?doMockInvoke(invocation,?e);
          ????????????}
          ????????}
          ????????return?result;
          ????}
          }


          public?class?MockInvoker<T>?implements?Invoker<T>?{

          ????@Override
          ????public?Result?invoke(Invocation?invocation)?throws?RpcException?{
          ????????String?mock?=?getUrl().getParameter(invocation.getMethodName()?+?"."?+?Constants.MOCK_KEY);
          ????????if?(invocation?instanceof?RpcInvocation)?{
          ????????????((RpcInvocation)?invocation).setInvoker(this);
          ????????}
          ????????if?(StringUtils.isBlank(mock))?{
          ????????????mock?=?getUrl().getParameter(Constants.MOCK_KEY);
          ????????}

          ????????if?(StringUtils.isBlank(mock))?{
          ????????????throw?new?RpcException(new?IllegalAccessException("mock?can?not?be?null.?url?:"?+?url));
          ????????}
          ????????mock?=?normalizeMock(URL.decode(mock));

          ????????//?直接包裝返回結(jié)果
          ????????if?(mock.startsWith(Constants.RETURN_PREFIX))?{
          ????????????mock?=?mock.substring(Constants.RETURN_PREFIX.length()).trim();
          ????????????try?{
          ????????????????Type[]?returnTypes?=?RpcUtils.getReturnTypes(invocation);
          ????????????????Object?value?=?parseMockValue(mock,?returnTypes);
          ????????????????return?new?RpcResult(value);
          ????????????}?catch?(Exception?ew)?{
          ????????????????throw?new?RpcException("mock?return?invoke?error.?method?:"?+?invocation.getMethodName()?+?",?mock:"?+?mock?+?",?url:?"?+?url,?ew);
          ????????????}
          ????????}

          ????????//?拋出異常
          ????????else?if?(mock.startsWith(Constants.THROW_PREFIX))?{
          ????????????mock?=?mock.substring(Constants.THROW_PREFIX.length()).trim();
          ????????????if?(StringUtils.isBlank(mock))?{
          ????????????????throw?new?RpcException("mocked?exception?for?service?degradation.");
          ????????????}?else?{
          ????????????????//?獲取自定義異常
          ????????????????Throwable?t?=?getThrowable(mock);
          ????????????????throw?new?RpcException(RpcException.BIZ_EXCEPTION,?t);
          ????????????}
          ????????}

          ????????//?自定義mock策略
          ????????else?{
          ????????????try?{
          ????????????????Invoker?invoker?=?getInvoker(mock);
          ????????????????return?invoker.invoke(invocation);
          ????????????}?catch?(Throwable?t)?{
          ????????????????throw?new?RpcException("Failed?to?create?mock?implementation?class?"?+?mock,?t);
          ????????????}
          ????????}
          ????}
          }

          5 產(chǎn)生疑問

          通過上述源碼我們知道,如果在mock屬性中配置force,那么不會執(zhí)行真正的業(yè)務(wù)邏輯,而是只執(zhí)行mock邏輯,這一部分比較容易理解:

          //?不執(zhí)行消費邏輯直接返回
          else?if?(value.startsWith("force"))?{
          ????if?(logger.isWarnEnabled())?{
          ????????logger.warn("force-mock:?"?+?invocation.getMethodName()?+?"?force-mock?enabled?,?url?:?"?+?directory.getUrl());
          ????}
          ????//?直接執(zhí)行mock邏輯
          ????result?=?doMockInvoke(invocation,?null);
          }

          但是如果是其它mock配置則首先執(zhí)行業(yè)務(wù)代碼,如果業(yè)務(wù)代碼發(fā)生異常了再執(zhí)行mock邏輯:

          try?{
          ????//?服務(wù)消費默認執(zhí)行FailoverClusterInvoker
          ????result?=?this.invoker.invoke(invocation);
          }?catch?(RpcException?e)?{
          ????if?(e.isBiz())?{
          ????????throw?e;
          ????}
          ????if?(logger.isWarnEnabled())?{
          ????????logger.warn("fail-mock:?"?+?invocation.getMethodName()?+?"?fail-mock?enabled?,?url?:?"?+?directory.getUrl(),?e);
          ????}
          ????//?服務(wù)消費失敗執(zhí)行mock邏輯
          ????result?=?doMockInvoke(invocation,?e);
          }

          這段代碼捕獲了RpcException異常,那么問題來了RpcException是什么類型的異常?我們使用自定義降級策略進行實驗,消費者代碼如下:

          package?com.java.front.dubbo.demo.consumer;
          import?com.java.front.demo.provider.HelloService;

          public?class?HelloServiceMock?implements?HelloService?{

          ????@Override
          ????public?String?sayHello(String?name)?throws?Exception?{
          ????????return?"mock";
          ????}
          }

          配置指定自定義策略并設(shè)置服務(wù)超時為2秒:

          <dubbo:reference?id="helloService"?mock="com.java.front.dubbo.demo.consumer.HelloServiceMock"?interface="com.java.front.demo.provider.HelloService"?timeOut="2000"?/>

          消費者測試代碼如下:

          public?static?void?testMock()?{
          ????ClassPathXmlApplicationContext?context?=?new?ClassPathXmlApplicationContext(new?String[]?{?"classpath*:META-INF/spring/dubbo-consumer1.xml"?});
          ????context.start();
          ????HelloService?helloServiceMock?=?(HelloService)?context.getBean("helloService");
          ????String?result?=?helloServiceMock.sayHello("JAVA前線");
          ????System.out.println("消費者收到結(jié)果="?+?result);
          }

          5.1 超時異常

          5.1.1 代碼實例

          我們在生產(chǎn)者業(yè)務(wù)代碼造成5秒的阻塞,模擬一個慢服務(wù):

          public?class?HelloServiceImpl?implements?HelloService?{

          ????public?String?sayHello(String?name)?throws?Exception?{
          ????????String?result?=?"hello["?+?name?+?"]";
          ????????//?模擬耗時操作5秒
          ????????Thread.sleep(5000L);
          ????????return?result;
          ????}
          }

          消費者執(zhí)行返回mock結(jié)果,說明超時異常屬于RpcException異常,可以被降級策略捕獲:

          消費者收到結(jié)果=mock

          5.1.2 源碼分析

          要分析超時異常為什么可以被降級策略捕獲,我們從以下兩個類分析。DefaultFuture.get方法采用了經(jīng)典多線程保護性暫停模式,并且實現(xiàn)了異步轉(zhuǎn)同步的效果,如果發(fā)生超時異常則拋出TimeoutException異常:

          public?class?DefaultFuture?implements?ResponseFuture?{

          ????@Override
          ????public?Object?get(int?timeout)?throws?RemotingException?{
          ????????if?(timeout?<=?0)?{
          ????????????timeout?=?Constants.DEFAULT_TIMEOUT;
          ????????}
          ????????//?response對象為空
          ????????if?(!isDone())?{
          ????????????long?start?=?System.currentTimeMillis();
          ????????????lock.lock();
          ????????????try?{
          ????????????????//?進行循環(huán)
          ????????????????while?(!isDone())?{

          ????????????????????//?放棄鎖并使當前線程阻塞,直到發(fā)出信號或中斷它或者達到超時時間
          ????????????????????done.await(timeout,?TimeUnit.MILLISECONDS);

          ????????????????????//?阻塞結(jié)束后再判斷是否完成
          ????????????????????if?(isDone())?{
          ????????????????????????break;
          ????????????????????}
          ????????????????????//?阻塞結(jié)束后判斷超過超時時間
          ????????????????????if(System.currentTimeMillis()?-?start?>?timeout)?{
          ????????????????????????break;
          ????????????????????}
          ????????????????}
          ????????????}?catch?(InterruptedException?e)?{
          ????????????????throw?new?RuntimeException(e);
          ????????????}?finally?{
          ????????????????lock.unlock();
          ????????????}
          ????????????//?response對象仍然為空則拋出超時異常
          ????????????if?(!isDone())?{
          ????????????????throw?new?TimeoutException(sent?>?0,?channel,?getTimeoutMessage(false));
          ????????????}
          ????????}
          ????????return?returnFromResponse();
          ????}
          }

          DubboInvoker調(diào)用了DefaultFuture.get方法,如果捕獲到上述TimeoutException則會拋出RpcException:

          public?class?DubboInvoker<T>?extends?AbstractInvoker<T>?{

          ????@Override
          ????protected?Result?doInvoke(final?Invocation?invocation)?throws?Throwable?{
          ????????try?{
          ????????????//?request方法發(fā)起遠程調(diào)用?->?get異步轉(zhuǎn)同步并進行超時驗證
          ????????????RpcContext.getContext().setFuture(null);
          ????????????Result?result?=?(Result)?currentClient.request(inv,?timeout).get();
          ????????????return?result;
          ????????}?catch?(TimeoutException?e)?{
          ????????????throw?new?RpcException(RpcException.TIMEOUT_EXCEPTION,?"Invoke?remote?method?timeout.?method:?"?+?invocation.getMethodName()?+?",?provider:?"?+?getUrl()?+?",?cause:?"?+?e.getMessage(),?e);
          ????????}?catch?(RemotingException?e)?{
          ????????????throw?new?RpcException(RpcException.NETWORK_EXCEPTION,?"Failed?to?invoke?remote?method:?"?+?invocation.getMethodName()?+?",?provider:?"?+?getUrl()?+?",?cause:?"?+?e.getMessage(),?e);
          ????????}
          ????}
          }

          源碼分析到這里已經(jīng)很清楚了,RpcException正是服務(wù)降級策略可以捕獲的異常,所以超時異常是可以被降級的。


          5.2 業(yè)務(wù)異常

          本文我們把非超時異常統(tǒng)稱為業(yè)務(wù)異常,例如生產(chǎn)者業(yè)務(wù)執(zhí)行時發(fā)生運行時異常可以歸為業(yè)務(wù)異常,下面我們進行試驗。

          5.2.1 代碼實例

          生產(chǎn)者執(zhí)行過程中拋出運行時異常:

          public?class?HelloServiceImpl?implements?HelloService?{

          ????public?String?sayHello(String?name)?throws?Exception?{
          ????????throw?new?RuntimeException("BizException")
          ????}
          }

          消費者調(diào)用直接拋出異常:

          java.lang.RuntimeException:?BizException
          ??at?com.java.front.dubbo.demo.provider.HelloServiceImpl.sayHello(HelloServiceImpl.java:35)
          ??at?org.apache.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)
          ??at?org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:56)
          ??at?org.apache.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:85)

          5.2.2 源碼分析

          我們發(fā)現(xiàn)服務(wù)降級對業(yè)務(wù)異常沒有生效,需要分析原因,我認為從以下兩點進行分析:

          (1) 消費者接收到什么消息

          public?class?DefaultFuture?implements?ResponseFuture?{
          ????public?static?void?received(Channel?channel,?Response?response)?{
          ????????try?{
          ????????????DefaultFuture?future?=?FUTURES.remove(response.getId());
          ????????????if?(future?!=?null)?{
          ????????????????future.doReceived(response);
          ????????????}?else?{
          ????????????????logger.warn("The?timeout?response?finally?returned?at?"
          ????????????????????????????+?(new?SimpleDateFormat("yyyy-MM-dd?HH:mm:ss.SSS").format(new?Date()))
          ????????????????????????????+?",?response?"?+?response
          ????????????????????????????+?(channel?==?null???""?:?",?channel:?"?+?channel.getLocalAddress()
          ???????????????????????????????+?"?->?"?+?channel.getRemoteAddress()));
          ????????????}
          ????????}?finally?{
          ????????????CHANNELS.remove(response.getId());
          ????????}
          ????}
          }

          response用來接收服務(wù)端發(fā)送的消息,我們看到異常信息存放在Response的exception屬性:

          Response?[id=0,?version=null,?status=20,?event=false,?error=null,?result=RpcResult?[result=null,?exception=java.lang.RuntimeException:?BizException]]

          (2) 異常在哪里被拋出

          我們知道消費者對象是一個代理對象,首先會執(zhí)行到InvokerInvocationHandler:

          public?class?InvokerInvocationHandler?implements?InvocationHandler?{
          ????private?final?Invoker?invoker;

          ????public?InvokerInvocationHandler(Invoker?handler)?{
          ????????this.invoker?=?handler;
          ????}

          ????@Override
          ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
          ????????String?methodName?=?method.getName();
          ????????Class[]?parameterTypes?=?method.getParameterTypes();
          ????????if?(method.getDeclaringClass()?==?Object.class)?{
          ????????????return?method.invoke(invoker,?args);
          ????????}
          ????????if?("toString".equals(methodName)?&&?parameterTypes.length?==?0)?{
          ????????????return?invoker.toString();
          ????????}
          ????????if?("hashCode".equals(methodName)?&&?parameterTypes.length?==?0)?{
          ????????????return?invoker.hashCode();
          ????????}
          ????????if?("equals".equals(methodName)?&&?parameterTypes.length?==?1)?{
          ????????????return?invoker.equals(args[0]);
          ????????}

          ????????//?RpcInvocation?[methodName=sayHello,?parameterTypes=[class?java.lang.String],?arguments=[JAVA前線],?attachments={}]
          ????????RpcInvocation?rpcInvocation?=?createInvocation(method,?args);

          ????????//?消費者Invoker?->?MockClusterInvoker(FailoverClusterInvoker(RegistryDirectory(invokers)))
          ????????Result?result?=?invoker.invoke(rpcInvocation);

          ????????//?結(jié)果包含異常信息則拋出異常?->?例如異常結(jié)果對象RpcResult?[result=null,?exception=java.lang.RuntimeException:?sayHelloError1?error]
          ????????return?result.recreate();
          ????}
          }

          RpcResult.recreate方法會處理異常,如果發(fā)現(xiàn)異常對象不為空則拋出異常:

          public?class?RpcResult?extends?AbstractResult?{

          ????@Override
          ????public?Object?recreate()?throws?Throwable?{
          ????????if?(exception?!=?null)?{
          ????????????try?{
          ????????????????Class?clazz?=?exception.getClass();
          ????????????????while?(!clazz.getName().equals(Throwable.class.getName()))?{
          ????????????????????clazz?=?clazz.getSuperclass();
          ????????????????}
          ????????????????Field?stackTraceField?=?clazz.getDeclaredField("stackTrace");
          ????????????????stackTraceField.setAccessible(true);
          ????????????????Object?stackTrace?=?stackTraceField.get(exception);
          ????????????????if?(stackTrace?==?null)?{
          ????????????????????exception.setStackTrace(new?StackTraceElement[0]);
          ????????????????}
          ????????????}?catch?(Exception?e)?{
          ????????????}
          ????????????throw?exception;
          ????????}
          ????????return?result;
          ????}
          }

          5.2.3 業(yè)務(wù)異常如何降級

          通過上述實例我們知道Dubbo自帶的服務(wù)降級策略只能降級超時異常,而不能降級業(yè)務(wù)異常。

          那么業(yè)務(wù)異常應(yīng)該如何降級呢?我們可以整合Dubbo、Hystrix進行業(yè)務(wù)異常熔斷,相關(guān)配置也并不復(fù)雜,大家可以網(wǎng)上查閱相關(guān)資料。


          6 文章總結(jié)

          本文我們首先介紹了服務(wù)雪崩這個場景,并且從非線性角度再次理解了服務(wù)雪崩。隨后我們總結(jié)了服務(wù)雪崩應(yīng)對方案,其中服務(wù)降級是應(yīng)對服務(wù)雪崩的重要方法之一。我們針對超時異常和業(yè)務(wù)異常兩種場,結(jié)合源碼深入分析了Dubbo服務(wù)降級的使用場景,希望本文對大家有所幫助。



          JAVA前線?


          歡迎大家關(guān)注公眾號「JAVA前線」查看更多精彩分享,主要包括源碼分析、實際應(yīng)用、架構(gòu)思維、職場分享、產(chǎn)品思考等等,同時也非常歡迎大家加我微信「java_front」一起交流學習


          瀏覽 63
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区二区精 | 国产一级黄片 | aigao | 亚洲香蕉影院视频在线 | 国产操|