1. 程式人生 > >Spring Cloud 異常處理

Spring Cloud 異常處理

轉載 : https://www.cnblogs.com/yish/p/5850813.html

一開始我想著是在Feign的ErrorDecoder上做自定義的異常處理,來實現根據http code丟擲各種異常。但是Feign與Hystrix結合之後,發現一個問題,只要服務呼叫丟擲了Throwable類就會觸發Hystrix的fallback(前提是配置了fallback)。想來想去都沒有想到怎麼利用這套機制來實現業務邏輯上的異常分支和伺服器處理異常。最後,靈光一現:

  我把異常分成兩大類,ClientException和ServerException。無論哪一種異常,都會導致對應服務本地事務回滾。當發生ClientException時,向客戶端返回的http code為200,當發生ServerException時,向客戶端返回的http code為500。

  ClientException是由客戶端引起的異常,比如輸入非法引數或者使用者登入輸入錯誤的密碼導致登入失敗而業務邏輯進入到非主線分支,這類問題其實都是客戶端導致的,只需要拒絕服務讓客戶端提交正確的引數即可恢復。因此當遇到這種異常的時候,其實業務邏輯是執行成功的,只不過進入了非主線分支而已。

  ServerException則是由伺服器上的邏輯漏洞或者其他什麼原因導致的無法由客戶端重新提交來糾正的錯誤,比如伺服器程式碼上寫的不嚴謹在某個地方報了空指標異常,這種問題客戶端無論提交多少次正確的引數都無法修正,因此是屬於伺服器內部錯誤。

  將異常分成這兩類之後,處理起來就清晰很多了。現在假設有兩個服務A、B,外部請求進來的時候,由閘道器服務轉發至A,A的處理過程中需要呼叫B,此時可能會發生以下幾種情況:

  1、A呼叫B之前發生ClientException,以Http Code 200返回結果至外部客戶端,http Body中會有固定的格式包含自定義的業務code、msg、data等。客戶端解析code後提示使用者msg並執行對應的邏輯來重新提交請求。通俗一點講就是登陸的時候輸入了錯誤的密碼,請求提交後頁面提示密碼錯誤,此時重新輸入正確密碼,提交,登入成功。

  2、A呼叫B之前發生ServerException,這個沒辦法,服務暫時不可用了。等重新發布來修復。

  3、A呼叫B時,B發生ClientException。此時Http Code 200返回結果至A,但是A在從http Body中提取data時,會發現業務code不等於操作成功的那個值,則A丟擲對應的ClientException,讓客戶端重新提交正確引數。

  4、A呼叫B時,B發生ServerException。則A的Feign 客戶端在解析的時候就會丟擲FeignException異常(由於http code為500)。此時有兩種選擇,A捕獲到FeignException,本地事務中斷回滾,並告知客戶端服務整體不可用,不過這樣體驗不好。還有一種選擇,就是當A的feign客戶端配置了fallback,則發生FeignException時會進入對應的fallback程式碼,那麼在這段程式碼裡可以向訊息佇列裡傳送這個呼叫請求!對應的B會監聽這個呼叫訊息,然而此時無論B呼叫多少次都會報錯,不過沒關係。緊急釋出一個補丁,修復導致ServerException的那個bug,然後部署上線。當新版本的B程式碼監聽到這個訊息時,請求就會處理成功,以達到最終一致性!

  5、A呼叫B成功後,A本地事務提交出錯,這個問題比較噁心,粗略想了一下A try catch一下然後呼叫B的補償介面來使B的資料恢復正常。

     最後,為了安全起見,微服務之前涉及到修改操作的介面,都要保證冪等性。最簡單的一個情況就是服務呼叫超時,Feign會自動重新發送請求幾次,那麼假設某服務響應過慢,則極有可能導致請求重複提交,若沒有做到冪等則會產生錯誤資料。另外一個就是基於訊息佇列來保證最終一致性,也有可能發生請求重複處理。