php接口冪等性


什么是冪等性

冪等性是系統服務對外一種承諾,承諾只要調用接口成功,外部多次調用對系統的影響是一致的。聲明為冪等的服務會認為外部調用失敗是常態,並且失敗之后必然會有重試。

什么情況下需要冪等

以SQL為例:

SELECT col1 FROM tab1 WHER col2=2,無論執行多少次都不會改變狀態,是天然的冪等。
UPDATE tab1 SET col1=1 WHERE col2=2,無論執行成功多少次狀態都是一致的,因此也是冪等操作。
UPDATE tab1 SET col1=col1+1 WHERE col2=2,每次執行的結果都會發生變化,這種不是冪等的。
insert into user(userid,name) values(1,'a') 如userid為唯一主鍵,即重復操作上面的業務,只會插入一條用戶數據,具備冪等性。
如userid不是主鍵,可以重復,那上面業務多次操作,數據都會新增多條,不具備冪等性。
delete from user where userid=1,多次操作,結果一樣,具備冪等性

 

http接口中的默認冪等性

大家都知道,http協議,根據客戶端請求服務端的不同操作分為多個請求方法:

GET    /users      # 獲取users列表

GET     /users/12     # 查看某個具體的users
POST    /users       # 新建一個users
PUT     /users/12     # 更新users 12
DELETE   /users/12     # 刪除users 12

get 方法(冪等)

get方法對資源並沒有任何的影響,雖然第一次請求完,可能會有數據更改(並非這次請求的修改),獲取的數據和第一次的不一致,但並不是它修改的數據,所以它在http協議中默認是冪等性的操作

post 方法(非冪等)

大家都知道,post一般用於提交表單,新增或修改數據,當提交多次時,會新增多次數據,所以它默認情況是非冪等性操作.

put方法(冪等)

put方法將替換原有的資源,由於是直接替換,無論多少次請求,替換的內容都是相同的,所以它是冪等性操作

delete方法(冪等)

delete針對於刪除某一個資源,再次刪除的話並不會額外刪除其他的資源,也不會新增資源,所以它是冪等性操作

 

在上面的http默認冪等性中,我們可以看出,post方法是非冪等性的(當然不止post一個).而且,在我們正常后端寫接口時,用的最多的應該是post/get去實現所有的數據操作方式.那么,現在有一些問題:

用戶A提交一個支付訂單,由於添加時網絡不好,用戶點擊了2次.

那么接口正常來說,是會新增2個訂單的,但是這樣就會嚴重影響用戶的體驗了

同理

用戶A想給B轉賬100元錢,但是不小心點了2下,如果沒做好冪等性,就會造成扣除2次100,扣200塊錢.

支付寶支付請求服務器充值回調,給用戶A增加余額,同時給支付寶返回OK告訴支付寶增加余額成功,但由於網絡異常,支付寶沒有收到OK信號,給服務器重發了一次充值回調,這時候給用戶A又增加了一次余額

 

如何保證冪等

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個請求都發生了額外的請求。

樂觀鎖機制

這種方法適合在更新的場景中,update t_goods set count = count -1 , version = version + 1 where good_id=2 and version = 1
根據version版本,也就是在操作庫存前先獲取當前商品的version版本號,然后操作的時候帶上此version號。我們梳理下,我們第一次操作庫存時,得到version為1,調用庫存服務version變成了2;但返回給訂單服務出現了問題,訂單服務又一次發起調用庫存服務,當訂單服務傳如的version還是1,再執行上面的sql語句時,就不會執行;因為version已經變為2了,where條件就不成立。這樣就保證了不管調用幾次,只會真正的處理一次。
樂觀鎖主要使用於處理讀多寫少的問題

唯一主鍵
這個機制是利用了數據庫的主鍵唯一約束的特性,解決了在insert場景時冪等問題。但主鍵的要求不是自增的主鍵,這樣就需要業務生成全局唯一的主鍵。

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

防重表
使用訂單號orderNo做為去重表的唯一索引,把唯一索引插入去重表,再進行業務操作,且他們在同一個事務中。這個保證了重復請求時,因為去重表有唯一約束,導致請求失敗,避免了冪等問題。這里要注意的是,去重表和業務表應該在同一庫中,這樣就保證了在同一個事務,即使業務操作失敗了,也會把去重表的數據回滾。這個很好的保證了數據一致性。

唯一ID
調用接口時,生成一個唯一id,redis將數據保存到集合中(去重),存在即處理過。

狀態機冪等

在設計單據相關的業務,或者是任務相關的業務,肯定會涉及到狀態機(狀態變更圖),就是業務單據上面有個狀態,狀態在不同的情況下會發生變更,一般情況下存在有限狀態機,這時候,如果狀態機已經處於下一個狀態,這時候來了一個上一個狀態的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態機的冪等。注意:訂單等單據類業務,存在很長的狀態流轉,一定要深刻理解狀態機,對業務系統設計能力提高有很大幫助

對外提供接口的api如何保證冪等

如銀聯提供的付款接口:需要接入商戶提交付款請求時附帶:source來源,seq序列號;source+seq在數據庫里面做唯一索引,防止多次付款(並發時,只能處理一個請求) 。
重點:對外提供接口為了支持冪等調用,接口有兩個字段必須傳,一個是來源source,一個是來源方序列號seq,這個兩個字段在提供方系統里面做聯合唯一索引,這樣當第三方調用時,先在本方系統里面查詢一下,是否已經處理過,返回相應處理結果;沒有處理過,進行相應處理,返回結果。注意,為了冪等友好,一定要先查詢一下,是否處理過該筆業務,不查詢直接插入業務系統,會報錯,但實際已經處理了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM