1. 程式人生 > >dubbo異常處理(一)

dubbo異常處理(一)

dubbo的異常處理網上描述的文章很多,複製黏貼也不在少數.在這裡記錄下自己的一些體會.

還是帶著問題(目錄)來寫吧.

1.dubbo官方推薦的異常處理方式是什麼?

2.dubbo處理異常的邏輯是什麼樣的?為什麼要這樣處理?

3.丟擲自定義異常有哪些方式?

4.在dubbo:provider中設定filter="-exception", 去掉異常的filter會怎麼樣?

5.最終採用的異常處理方案

1.dubbo官方推薦的異常處理方式是什麼?

在dubbo官方文件的服務化最佳實踐中,推薦的處理方式如下:


2.dubbo處理異常的邏輯是什麼樣的?為什麼要這樣處理?

dubbo的異常處理類是com.alibaba.dubbo.rpc.filter.ExceptionFilter 類,原始碼這裡就不貼了.歸納下對異常的處理分為下面幾類:

1)如果provider實現了GenericService介面,直接丟擲

2)如果是checked異常,直接丟擲

3)在方法簽名上有宣告,直接丟擲

4)異常類和介面類在同一jar包裡,直接丟擲

5)是JDK自帶的異常,直接丟擲

6)是Dubbo本身的異常,直接丟擲

7)否則,包裝成RuntimeException拋給客戶端

網上有些文章對7)的處理有疑問,不理解原因,其實就是為了防止客戶端反序列化失敗.前面幾種情況都能保證反序列化正常.

3.丟擲自定義異常有哪些方式?

丟擲自定義異常其實就是使用上面2中的邏輯.所以相對應的有以下幾種方式:

1)provider實現GenericService介面.(我沒試過這種方式,應該是要自己實現$invoke()方法,網上

說直接把$invoke()方法廢棄掉不知道是怎麼處理的,直接返回null肯定是不行的)

2)自定義異常宣告為checked異常(這沒啥說了,不過一般自定義異常都是unchecked)

3)在方法簽名上宣告丟擲異常(這種基本上所有介面都要寫,麻煩)

4)異常類和介面類在同一jar包裡(存在鏈式呼叫時,這種可能不適用)

5)自定義異常的包名以java.或javax.開頭(dubbo判斷jdk自帶異常的條件,一般專案都有自己的命名規範,這樣乾的估計很少)

除了上面對應的,還可以用一種奇葩的方式,直接去掉異常的filter,如下:

6) <dubbo:provider filter="-exception" />

4.在dubbo:provider中設定filter="-exception", 去掉異常的filter會怎麼樣?

我在處理自定義異常的時候,覺得3中提到前5種方式都不太適合,所以使用了這種方式.

這種方式對provider中丟擲的異常不做任何處理,直接返回給consumer,會遇到一些奇怪的問題;

例如,在provider中有以下程式碼:

  1. String respone = "avs";  
  2. JSONObject resultjson = JSONObject.fromObject(respone);  

這段程式碼會丟擲 net.sf.json.JSONException ,但是在provider中不會列印任何異常資訊.

而在consumer中,會報出下面的錯誤:

  1. com.alibaba.com.caucho.hessian.io.HessianFieldException: org.apache.commons.lang.exception.NestableRuntimeException.delegate: 'org.apache.commons.lang.exception.NestableDelegate' could not be instantiated  
  2.     at com.alibaba.com.caucho.hessian.io.JavaDeserializer.logDeserializeError(JavaDeserializer.java:671)  
  3.     at com.alibaba.com.caucho.hessian.io.JavaDeserializer$ObjectFieldDeserializer.deserialize(JavaDeserializer.java:400)  
  4.     at com.alibaba.com.caucho.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:233)  
  5.     at com.alibaba.com.caucho.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:157)  
  6.     at com.alibaba.com.caucho.hessian.io.SerializerFactory.readObject(SerializerFactory.java:397)  
  7.     at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObjectInstance(Hessian2Input.java:2070)  
  8.     at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2005)  
  9.     at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:1990)  
  10.     at com.alibaba.dubbo.common.serialize.support.hessian.Hessian2ObjectInput.readObject(Hessian2ObjectInput.java:88)  
  11.     at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:92)  
  12.     at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:109)  
  13.     at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.decodeBody(DubboCodec.java:97)  
  14.     at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:126)  
  15.     at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:87)  
  16.     at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec.decode(DubboCountCodec.java:46)  
  17.     at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.messageReceived(NettyCodecAdapter.java:134)  
  18.     at org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:80)  
  19.     at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564)  
  20.     at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559)  
  21.     at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274)  
  22.     at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261)  
  23.     at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:349)  
  24.     at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:280)  
  25.     at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:200)  
  26.     at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)  
  27.     at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:44)  
  28.     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)  
  29.     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)  
  30.     at java.lang.Thread.run(Thread.java:745)  
  31. Caused by: com.alibaba.com.caucho.hessian.io.HessianProtocolException: 'org.apache.commons.lang.exception.NestableDelegate' could not be instantiated  
  32.     at com.alibaba.com.caucho.hessian.io.JavaDeserializer.instantiate(JavaDeserializer.java:275)  
  33.     at com.alibaba.com.caucho.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:155)  
  34.     at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObjectInstance(Hessian2Input.java:2067)  
  35.     at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:1592)  
  36.     at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:1576)  
  37.     at com.alibaba.com.caucho.hessian.io.JavaDeserializer$ObjectFieldDeserializer.deserialize(JavaDeserializer.java:396)  
  38.     ... 27 more  
  39. Caused by: java.lang.reflect.InvocationTargetException  
  40.     at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)  
  41.     at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)  
  42.     at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)  
  43.     at java.lang.reflect.Constructor.newInstance(Constructor.java:526)  
  44.     at com.alibaba.com.caucho.hessian.io.JavaDeserializer.instantiate(JavaDeserializer.java:271)  
  45.     ... 32 more  
  46. Caused by: java.lang.IllegalArgumentException: The Nestable implementation passed to the NestableDelegate(Nestable) constructor must extend java.lang.Throwable  
  47.     at org.apache.commons.lang.exception.NestableDelegate.<init>(NestableDelegate.java:112)  
  48.     ... 37 more  

這是個因為異常傳遞引起的錯誤,原異常資訊也丟失了,無法定位問題.

為了解決這個問題,想到的方案是在provider中,把所有入口方法都進行catch處理,轉換為自定義異常.

想當然的就用spring的AOP功能來實現.(解決一個bug,引入另外的bug大笑).

  1. <beanid="serviceEx"class="com.dingcheng.common.exception.ServiceExceptionHandler"/>
  2. <aop:config>
  3.     <aop:aspectid="exServiceAop"ref="serviceEx">
  4.            <aop:pointcutid="exParam"expression="execution(* com.dingcheng.*.service.*Service.*(..))"/>
  5.            <aop:after-throwingpointcut-ref="exParam"method="doThrowing"throwing="ex"/>
  6.        </aop:aspect>
  7. </aop:config>


使用AOP進行異常攔截後引發了另外一個問題,就是ServiceA.a()裡面呼叫ServiceB.b()這種情況.

b()裡丟擲異常,aop處理一次,然後a再丟擲異常,aop再處理一次,多次呼叫就多次處理.

理想的解決方案是像事務管理那樣可以配置傳播性,但是這個不支援這種配置,

所以這種方案也不夠好.

5.最終採用的異常處理方案

丟擲一個自定義異常有這麼麻煩嗎? 主要原因是dubbo沒有支援的原因.

既然這樣,我們把dubbo變的支援不就可以了?

是的.把原始碼改一下就OK了.如下圖:


在異常處理這裡,加上自定義異常處理的程式碼.

或者直接將112行的RuntimeException替換成自己的自定義異常!

修改原始碼後,可以替換maven倉庫的jar,或者是在自己專案裡面建一個ExceptionFilter,包名和dubbo的相同,用來覆蓋掉dubbo的

(如果替換112行為自定義異常,則要引入自定義的包等).

這樣就從根本上解決了異常處理的問題.後續有其他問題,也可以直接修改.