1. 程式人生 > >重試和冪等

重試和冪等

摘要

重試是一種保障業務執行的容錯機制,比如頁面查詢、資料匯出等業務場景,如果某個微服務出現異常,可以將請求動態路由到其他的服務。但是對於寫的業務場景,就會導致很多問題,比如重複訂購,重複生成記錄,甚至重複扣費。本文重點討論如果避免寫的重試

重試

常見的重試場景

在這裡插入圖片描述

(1)頁面操作
重複點選新增或者修改按鈕
重複匯入同一批資料,如Excel
頁面重新整理導致重複提交

(2)定時任務
定時任務Cron表示式設定有問題,導致短時間內重複處理同一批資料。
定時任務中某個分片失敗了,重複執行。

(3)開放API
http超時重發
異常重發
黑客等惡意重複傳送同一訊息

(4)訊息佇列
業務異常導致重複傳送同一條到同一個佇列。
訊息處理失敗後放到Retry佇列,然後重複消費。
死信佇列裡面的訊息重複消費。

(5)微服務框架
容錯機制選用不當 比如upate 和 insert 介面選用 faileover,導致超時重發多次請求。

上面的場景有可能組合到一起出現,如下圖:
在這裡插入圖片描述
系統如果不對重試做控制,在極端情況下,會導致系統併發量瞬間暴增,導致出現大量髒資料甚至系統癱瘓。

冪等

冪等設計是解決寫重試問題重要手段。下面針對各種場景以及我們系統現實情況,分析如何實現冪等

頁面 和API 冪等實現

首先應該遵循restfull 介面設計規範,‘寫’請求,最好是對單一資源的操作,如果是批量操作,必須有批次號,以及詳細的操作記錄。請求訊息體中攜帶訊息ID,(訊息的完整性和安全性可以通過hash演算法保證,不在本次討論範圍內)。在控制層,針對訊息ID做重複校驗,這樣可以做到技術上的冪等。
在這裡插入圖片描述

定時任務冪等

設定唯一訊息ID,可能不起作用了,因為job 可以重複執行,生成新的訊息ID。這時只能根據業務特點,針對特定的業務型別,新增業務操作日誌。比如定時為某些使用者下發訂單的場景,可以將使用者訂購資訊新增到單獨日誌表中或者redis中,且這些日誌資訊應該是跟業務無關的,只用來做防止重複訂購的校驗,使用完後可以定時清理掉,或者自動失效,避免堆積太多的垃圾資料。訊息的結構可以包括:使用者標識、業務標識、操作時間、操作結果,其中業務型別就表示這是定製化的重複校驗,用來保證業務上的冪等。
在這裡插入圖片描述

mq的冪等消費

防止訊息重複消費的設計方式跟定時任務冪等的設計方式一樣,只能根據特定的業務型別,做到業務邏輯上的冪等。

微服務框架

要謹慎的選擇重試策略和叢集方式。
對於系統間呼叫鏈比較短的場景,可以取消重試,然後整個資料流向設計成快速失敗的(failefast), 比如我們的系統,目前最長的業務流程也就是呼叫5個功能模組(微服務)。
但是必須有其他的容錯機制,這裡容錯機制不僅僅是微服務架構上的容錯,也是業務流程整體設計上的容錯,例如:每個請求都帶有請求日誌,記錄請求狀態和時間。對於異常的請求,可以手動重試,也可以自動重試,或者將整個過程回滾,這就是業務流程設計上的容錯。微服務架構容錯機制則是重試並新增熔斷器,重試可以用前提是所有介面都是冪等的,但是熔斷器也是個雞肋,很難自動控制,若果熔斷策略選用不當還會起反作用,甚至不如APM監控+分散式配置功能開關組合策略。所以對於完整性要求比較的高的業務場景,可以取消重試,去掉熔斷器,但是要在業務流程的入口處加上限流機制,防止過載。
我們目前的措施是在入口處做限流。因為我們的系統主要還是給公司內部的運營人員用的,運營會有很多批量操作,這些操作都是短時間大批量資料的處理,在不影響系統正常運作的前提下,我們直接在功能入口處做限流,限制操作頻次,限制資料量。
還有一種情況是定時任務處理大批量的資料,對這種場景我們並沒有做限流,否則會影響處理效率。但是所有的介面都設計成冪等的。

遺留問題

請求日誌主要做冪等校驗的,應該和業務資料隔離開來,目前系統還是放在一起的。