介面多次呼叫結果不一致?如何保證介面的冪等性

2021-10-09 18:53:36 字數 3003 閱讀 9555

冪等性最早是數學裡面的乙個概念,後來被用於計算機領域。

介面的冪等性實際上就是介面可重複呼叫,在呼叫方多次呼叫的情況下,介面最終得到的結果是一致的。

在微服務架構下或者呼叫外部介面時,我們在完成乙個訂單流程時經常遇到下面的場景:

乙個訂單建立介面,第一次呼叫超時了,然後呼叫方重試了一次

在訂單建立時,我們需要去扣減庫存,這時介面發生了超時,呼叫方重試了一次

當這筆訂單開始支付,在支付請求發出之後,在服務端發生了扣錢操作,介面響應超時了,呼叫方重試了一次

乙個訂單狀態更新介面,呼叫方連續傳送了兩個訊息,乙個是已建立,乙個是已付款。但是你先接收到已付款,然後又接收到了已建立

在支付完成訂單之後,需要傳送一條簡訊,當一台機器接收到簡訊傳送的訊息之後,處理較慢。訊息中介軟體又把訊息投遞給另外一台機器處理

為了解決以上問題,就需要保證介面的冪等性。

總的來說在增刪改查4個操作中,尤為注意就是增加或者修改

查詢操作

​ 查詢對於結果是不會有改變的,查詢一次和查詢多次,在資料不變的情況下,查詢結果是一樣的。select是天然的冪等操作

刪除操作

​ 刪除一次和多次刪除都是把資料刪除。(多次操作,結果一樣,具備冪等性。注意刪除的資料不存在時,可能返回結果不一樣)

更新操作

​ 修改在大多場景下結果一樣,但是如果是增量修改是需要保證冪等性的,如下例子:

​ 把表中 id 為 *** 的記錄的a字段值設定為1,這種操作不管執行多少次都是冪等的

​ 把表中 id 為 *** 的記錄的a字段值增加1,這種操作就不是冪等的

新增操作

​ 增加在重複提交的場景下會出現冪等性問題,如以上的支付問題

token機制

1、服務端提供了傳送token的介面。我們在分析業務的時候,哪些業務是存在冪等問題的,就必須在執行業務前,先去獲取token,伺服器會把token儲存到redis中。

2、然後呼叫業務介面請求時,把token攜帶過去,一般放在請求頭部。

3、伺服器判斷token是否存在redis中,存在表示第一次請求,然後刪除token,繼續執行業務。

4、如果判斷token不存在redis中,就表示是重複操作,直接返回重複標記給client,這樣就保證了業務**,不被重複執行。

關鍵點 先刪除token,還是後刪除token。

後刪除token:如果進行業務處理成功後,刪除redis中的token失敗了,這樣就導致了有可能會發生重複請求,因為token沒有被刪除。這個問題其實是資料庫和快取redis資料不一致問題。

先刪除token:如果系統出現問題導致業務處理出現異常,業務處理沒有成功,介面呼叫方也沒有獲取到明確的結果,然後進行重試,但token已經刪除掉了,服務端判斷token不存在,認為是重複請求,就直接返回了,無法進行業務處理了。

先刪除token可以保證不會因為重複請求,業務資料出現問題。出現業務異常,可以讓呼叫方配合處理一下,重新獲取新的token,再次由業務呼叫方發起重試請求就ok了。

token機制缺點

業務請求每次請求,都會有額外的請求(一次獲取token請求、判斷token是否存在的業務)。其實真實的生產環境中,1萬請求也許只會存在10個左右的請求會發生重試,為了這10個請求,我們讓9990個請求都發生了額外的請求。

唯一索引

建立唯一性索引的表來實現冪等性,在每次執行業務之前,先執行插入操作,因為唯一字段就是主鍵id,因此如果重複插入的話會觸發唯一約束而導致插入失敗,在這種情況下(插入失敗我們就可以判斷他為重複提交的請求)

如果是分庫分表場景下,路由規則要保證相同請求下,落在同乙個資料庫和同一表中,要不然資料庫主鍵約束就不起效果了,因為是不同的資料庫和表主鍵是不相關的。

唯一id

呼叫介面時,生成乙個唯一id,redis將資料儲存到集合中(去重),存在即處理過。

通過悲觀鎖來實現冪等性

首先來判斷資料的狀態:

select status from table_name where id=

'***'

;

然後再進行操作:

insert

into table_name(id)

values

('***');

update table_name set

status

='***'

;

這種情況是非原子操作,所以在高併發環境下可能會造成乙個業務被執行兩次的問題。

當乙個程式在執行中時,而另乙個程式也開始狀態判斷的操作。

所以需要使用事務

開啟事務

select

status

from table_name where id=

'***'

;insert

into table_name(id)

values

('***');

update table_name set

status

='***'

;提交事務

注意:mysql資料庫必須選用 innodb 儲存引擎,且 id 字段一定要是主鍵或者是唯一索引,不然會鎖表,影響其他業務執行

分布式鎖實現

分布式鎖實現冪等性的邏輯是,在每次執行方法之前判斷,是否可以獲取到分布式鎖,如果可以,則表示為第一次執行方法,否則直接捨棄請求即可。

需要注意的是分布式鎖的key必須為業務的唯一標識,通常適用redis或者zookeeper來實現分布式鎖。

參考鏈結

參考鏈結

參考鏈結

參考鏈結

快取不一致

當程式在執行過程中,會將運算需要的資料從主存複製乙份到cpu的快取記憶體當中,那麼cpu進行計算時就可以直接從它的快取記憶體讀取資料和向其中寫入資料,當運算結束之後,再將快取記憶體中的資料重新整理到主存當中。舉個簡單的例子 i i 1。當執行緒執行這個語句時,會先從主存當中讀取i的值,然後複製乙份到...

vc6 debug release 結果不一致問題

網上找了些資料,但未能解決我的問題 i.記憶體分配問題 1.變數未初始化。為debug中會自動給變數初始化found false,而在release版中 則不會。所以盡可能的給變數 類或結構初始化。2.資料溢位的問題 如 char buffer 10 int counter lstrcpy buff...

version magic 不一致問題

碰到乙個問題,在開發過程中發現以前編譯的模組載入失敗了。wlan version magic 4.1.15 gfb2dbf6 smp preempt mod unload armv7 p2v8 should be 4.1.15 ge5de83b dirty smp preempt mod unloa...