1. 程式人生 > >服務介面的冪等設計

服務介面的冪等設計

介面冪等

什麼是介面冪等?就是一個介面,被重複呼叫多次,卻能夠保證對系統內部產生的影響是一致的,也就是呼叫多次和呼叫一次,資料的變化是一樣的,是相同的,不會因為呼叫多次而出現任何資料問題。分散式系統中,介面冪等性是系統可行性論證的第一個步驟。很多地方需要把介面設計成冪等。

思路基本上是3種:

1 當第N(N>1)次請求過來時,系統要能知道,這個業務我們已經處理過了,相同的請求我們忽略掉就好了
2 當第N(N>1)次請求過來時,不管三七二十一,執行執行之,底層的資料介面層面保證其冪等就好了
3 從源頭上避免請求重複提交。當然,這個有一定的限制。對於使用者重複點選,那麼容易避免,代理端可以進行各種過濾,去重。但對於mq的情況等,可能無法避免。

雖然概念上很接近,我們也很容易混為一談,但服務介面的冪等和資料介面的冪等 ,細分開來還是有所不同的。 服務介面(我們的service層)可能包括了 對資料的操作,對檔案的操作,對網路的操作,對cpu、記憶體的計算,還有對其他服務的操作; 而資料介面(我們的dao層)常常限於對資料庫表的CRUD(這裡不討論廣義的“資料”的定義,而是記憶體、快取、檔案、資料庫分開討論),及其複合操作

資料介面層面的冪等設計

所有的資料介面都可以歸結為增刪改查四大類;當然,下面我們對這四大類介面進行分析;

查詢和刪除
查詢和刪除業務,天然的具有冪等的特性;

1. 查詢
在資料不變的情況下,查詢一次和查詢多次,查詢結果是一樣的;

2. 刪除
刪除一次和多次刪除的結果都是把資料刪除;(如果第二次刪除返回0 rows affected之類的,那麼忽略即可)

3. 新增
我們可以把關鍵的 業務id 設定成唯一索引,這樣,第二次會失敗,唯一索引約束錯誤的話,呼叫方忽略即可。否則就出現了多條資料,一條正確的,其餘的是髒資料。

主要就是通過業務的相關的欄位組成的一個 唯一性的約束。執行訊息處理之前可以先根據這個唯一約束是否存在,如果存在,說明已經執行過了,忽略即可,否則把這個唯一約束以某種方式 儲存起來。大部分情況 都會存在 至少一個 業務的唯一性的約束, 比如使用者的 郵箱不能重複吧。 

 

 

4. 更新
如果是冪等的更新操作,比如update table1 set f1 = v1,我們可以不用管。因為這些操作本身是冪等的。 否則我們可能需要對update 語句進行稍稍的改寫,增加where 條件,也就是樂觀鎖的方式。比如 update table1 set f1 = v1 where f1 = v0 (v0 是初始值)。 這樣第一次update會成功,後面的 就會失敗。我們需要儘量的把那些非冪等的update sql改寫。 但是這樣,有一定限制就是我們需要 更多的引數,比如這個初始值。 而且,我們不能排除某些操作是 不能改寫的, 比如給使用者增加積分等等, 原始積分就是限制的引數, 如果不能提供,那麼無法改寫。

 

服務介面層面的冪等設計

查詢業務

我們可以首先把資料載入到快取,然後儘量保證一個高可用的快取,同時在 新增、更新、刪除的時候維護快取。

新增業務
新增業務類介面,我們要解決如下兩個問題
1. 同一個使用者用同樣的資料多次請求同一個介面(不管是什麼原因多次提交,他應該只請求一次)
2. 不同使用者的提交同樣的資料請求同一個介面;
第一個問題可以通過防重複提交來解決;業務資料連同Token,一起提交給介面,同一個Token,只能被處理一次(這裡要注意,只能被處理一次,應該改成只能被正確的處理一次,也就是說,我們應該快取某次新增業務處理的結果,如果上一次請求時出現某些異常,比如資料庫連線失敗,使用者再次提交的時候,我們應該放行使用者的這次請求,當然有些異常就不需要放行了,比如提交的業務資料不對等);
第二個問題是無法解決的,一個開放的系統,不能杜絕兩個不同的客戶端(使用者)同時請求;但是可以交給資料的最後防線,儲存層;通過唯一索引或唯一組合索引可以防止新增資料存在髒資料 (當表存在唯一索引,併發時新增報錯時,再查詢一次就可以了,資料應該已經存在了,返回結果即可) ;

注意:
Token防重複提交,只需要閘道器這層控制即可;Token的處理機制,還需要快取呼叫的處理結果,以判斷是否需要放行後續的重試請求;

更新業務

系統中的大部分業務都可以歸屬到更新業務,比如禁用使用者、電商秒殺等等,只要是有更新操作的,不管是不是還有其他的操作,都歸結到更新業務;
更新業務介面,不僅需要有表單防重複提交的驗證,還需要有下面這些更精細的控制,以防止高併發環境中出現髒讀,幻讀等引起錯誤的資料更新結果;
更新業務介面冪等性解決方案一般是通過各種層面的鎖和CAS機制;

悲觀鎖
悲觀鎖,select for update,整個執行過程中鎖定要操作的記錄;

樂觀鎖
更新業務的介面,比如訂單付款等,需要綜合使用盡可能多的資訊來逐步驗證逐步減少直至杜絕重複訊息重複處理的概率;基本思路是CAS(Compare And Set);
可以參考下面的兩篇文章體會一下:
1. 《架構師之路-庫存扣多了,到底怎麼整》
2. 訂單操作,利用訂單編號和訂單的狀態機(序列號)

測試用例
通過下面的方法可以初步驗證介面冪等性的健壯性:
1. 同一個請求,多次提交到同一臺節點,多次提交到不同的節點
2. 同一個請求,同時到達同一個節點,同時到達到不同的節點
3. 有邏輯先後順序的訊息、請求亂序的處理,比如建立訂單的請求和支付訂單的請求,不能保證第一個請求先於第二個請求到達伺服器;
--------------------- 摘抄至 https://blog.csdn.net/xichenguan/article/details/78085801-------------

 

訊息消費流程的異常點

訊息的消費確認流程中,任何一個環節都可能會出問題!

  • 方法:對於未確認的訊息,採用按規則重新投遞的方式進行處理。
  • 問題:訊息的重複傳送會導致業務處理接口出現重複呼叫的問題。

訊息重複傳送的原因

被動方應用接收到訊息,業務處理完成後應用出問題,訊息中介軟體不知道訊息處理結果,會重新投遞訊息。
被動方應用接收到訊息,業務處理完成後網路出問題,訊息中介軟體收不到訊息處理結果,會重新投遞訊息。
被動方應用接收到訊息,業務處理時間過長,訊息中介軟體因訊息超時未確認,會再次投遞訊息。
被動方應用接收到訊息,業務處理完成,訊息中介軟體問題導致收不到訊息處理結果,訊息會重新投遞。
被動方應用接收到訊息,業務處理完成,訊息中介軟體收到了訊息處理結果,但由於訊息儲存故障導致訊息沒能成功確認,訊息會再次投遞。

怎麼實現消費方的訊息冪等

1 根據業務來實現冪等,前面已經說過。

2 增加訊息表來實現冪等,主要就是說,通過訊息的id 或其他業務約束,存放到訊息表,然後消費的時候先判斷是否已經存在,然後 就類似前面的操作了。

其實這兩種都差不多的,有時候可能區別不了。

 

參考:

https://blog.csdn.net/xichenguan/article/details/78085801 

https://blog.csdn.net/qq_27384769/article/details/79307340