轉(zhuǎn)自:http://blog.csdn.net/wzl18/article/details/51249018
dubbo有自己的異常處理機(jī)制,當(dāng)服務(wù)端拋出一個dubbo可以處理傳遞的異常時,會直接在客戶端上再次拋出,由開發(fā)者自己去處理。注意:這里說的不是所有異常,而是dubbo可以處理傳遞的異常,具體這個后邊再說。
先看兩段代碼,接口代碼:

簡單實(shí)現(xiàn):

這兩段代碼很簡答,先看getByNick方法,根據(jù)用戶名稱獲取用戶信息,里面有一個去空格的操作(主要為了觸發(fā)異常),正常調(diào)用是沒有問題的,但如果傳入null,就會拋出很常見且低級的空指針異常。我們看下調(diào)用代碼:

運(yùn)行后,首先會打印{"id":0,"name":"某某"},然后出現(xiàn)java.lang.NullPointerException,一切在我們的預(yù)料內(nèi),dubbo把服務(wù)端的空指針異常傳遞給客戶端了。
正常來說,空指針異常是不應(yīng)該出現(xiàn)的,而且客戶端遇到這個錯誤肯定直接懵了,所以我們做下簡單的修改,服務(wù)端代碼:

客戶端再次調(diào)用結(jié)果:

結(jié)果友好了多,甚至你可以直接對exception獲取異常信息作為輸出。
上邊提到過當(dāng)服務(wù)端拋出一個dubbo可以處理傳遞的異常時,會直接在客戶端上再次拋出,但不是所有的異常都是dubbo可以處理傳遞的,如下邊的代碼:

這里我們模擬拋出了一個mybatis的異常,在客戶端調(diào)用會像上邊的結(jié)果一樣嗎?答案是否定的,看下輸出結(jié)果:

不要感覺奇怪,這個也是在可以接受的范圍內(nèi),因?yàn)镻ersistenceException異常類在客戶端是不存在的,所以不可能接收到PersistenceException異常,dubbo把他進(jìn)行了封裝。
針對這點(diǎn),在接口包中里面定義了一個全局的異常類,注意一定是接口所在的工程中,如:UicException(用戶模塊異常),這種方案也是官方建議的,服務(wù)端代碼如下:

錯誤信息如下

這個正是我們想要的異常信息,上邊特別提到異常一定要在接口所在的工程中,如果異常類不在接口工程中,而是在另一個服務(wù)端和客戶端都引入的包中呢?我們曾經(jīng)碰到這樣一個情況,有一個common的異常類放在一個很底層的工具包內(nèi),接口工程引入了這個包,在服務(wù)端拋出的異常都都是這個commonexception,一廂情愿的認(rèn)為客戶端會正常去捕獲處理commonexception。
但結(jié)果很意外,客戶端出現(xiàn)的異常跟上邊拋出的PersistenceException情況一樣,dubbo用RuntimException進(jìn)行了包裝,我們無法從異常中獲取有效的信息!遇到這種情況有點(diǎn)發(fā)懵,這個異常類在客戶端和服務(wù)端都有呀,為啥不能正確接收呢。還好之前看dubbo源碼的時候大概記得異常處理的位置,很好找到了目標(biāo)代碼:


看完源碼以后,做出了新的設(shè)計(jì),CommonException不變,各個接口模塊(maven工程為單位)單獨(dú)定義異常對象繼承CommonException,每個模塊拋出自己的模塊異常(如用戶模塊拋出UicException),客戶端中用CommonException統(tǒng)一捕獲處理。
這里還要定義兩個攔截器,首先是服務(wù)端,保證所有拋出的異常是當(dāng)前模塊的異常,代碼如下:

其次是客戶端的,保證異??梢哉_的友好的輸出,所有CommonException可以直接輸出(獲取根據(jù)錯誤碼獲取錯誤信息),非CommonException異常根據(jù)自己需要去處理,如果是dubbo自帶異??隙ㄒ帘萎惓P畔?,如打印日志后輸出“網(wǎng)絡(luò)異?!?。
還有另一種dubbo調(diào)用方案,普通service層外邊嵌套一層用來做dubbo的服務(wù),普通service層處理了事務(wù)之類,dubbo服務(wù)層每一個方法都是客戶端要引用的,直接調(diào)用普通service層方法,但做了手動的try catch處理,封裝自己的返回碼,客戶端只需要根據(jù)返回碼去做處理,這種開發(fā)成本和文檔成本有點(diǎn)高,沒太深入去考慮。
以上是我自己工作中的dubbo異常實(shí)踐,以后會繼續(xù)寫些其他的心得,記錄自己的成長。