遇到的問題
目前系統中,為了保證服務的可用性。充斥著大量的重試和降級相關的代碼。遇到以下幾個問題:
- 重試和降級的邏輯需要重復編寫,容易產生錯誤。
- 重試和降級邏輯并沒有很好的監控和管理。發現問題比較被動,發生問題后手動修數據比較麻煩。
如何解決問題
- 解決問題一要做的就是對通用的邏輯進行抽象。在需要使用重試的場景時,通過簡單的配置聲明就能解決問題。
- 解決問題二則需要對哪些服務調用失敗了,重試過幾次,采用的是什么降級策略進行記錄。對一些關鍵的事件進行告警。例如調用失敗,進行過降級等。對于需要人工參與處理的場景,則需要提供入口進行人工補償。如果有必要還可以單獨開發一個系統來維護和管理。
關于重試
適合通過重試來保證可用性的場景有:
- 只讀操作或冪等寫操作。例:假設調用服務A的超時時間是2秒,服務A的執行時間是3秒。在A服務執行結束之前調用方超時重試。如果服務A不是冪等的就會出現問題。
- 不需要外部數據變更協助來完成的操作。例:如果調用服務A返回的是參數校驗類的錯誤,則不管重試幾次也不會成功。
重試的技術方案
- 固定的for循環
for (int i = 0; i < RETRY_TIMES; i++) {
try {
//服務調用
break;
} catch (Exception e) {
if (i == RETRY_TIMES - 1) {
throw e;
}
}
}
優點:簡單直接
缺點:重復代碼較多,容易產生錯誤,僅支持簡單的重試策略。
- 利用Spring-retry或guava-retryer
優點:功能完整,對重試涉及的相關邏輯,進行了良好的抽象。
缺點:有一些學習和改造的成本。 - 完全異步的解決方案。將錯誤消息推送到另一個系統或記錄到數據庫中稍后重試。
優點:將重試邏輯與正常業務邏輯完全拆分開,可以分別維護互不影響。消息可以在故障的時候堆積起來,等故障恢復了再慢慢來處理,減少人工介入的成本。
缺點:引入了額外的復雜度。會增加調試和維護成本。只適合冪等寫操作并且不需要同步返回結果的情況。
實際場景
只讀服務
可采用方案2來解決,會增加響應時間。
冪等寫需要同步返回結果的服務
可采用方案2來解決,會增加響應時間。
冪等寫不需要同步返回結果的服務。
可采用方案2,3來解決。
關于降級
采用降級方案時,需考慮的主要問題是方案的合理性和可能會產生的影響。
使用降級的場景
- 通過遠程服務讀取操作降級為從本地緩存讀取。犧牲的是數據一致性。
- 通過遠程服務讀取操作改為返回默認值。犧牲的是正確性。
- 買二等座降級為買一等座,類似的業務降級則要和相關的利益方進行溝通確認。
- 對于不重要的服務在發生問題時直接關閉開關,保證關鍵服務的正常使用。
犧牲的是非關鍵業務或數據的可用性。
技術方案
可以使用Spring-try項目配合開關來實現。有損服務更多的是取舍的考慮。
總結
以上就是工作中對相關問題的思考記錄,等以后有更多的理解了再補充完整。