接口幂等性思路
概念
接口冪等性就是用戶對于統(tǒng)一操作發(fā)起的一次請求或多次請求的結果是一致的,不會因為多次點擊而產(chǎn)生了副作用。
哪些場景需要保證接口的冪等性?
用戶多次點擊按鈕。
用戶頁面回退再次提交
微服務之間相互調(diào)用,由于網(wǎng)絡波動卡頓,導致feign觸發(fā)重試機制。
其他情況...
天然冪等情況
以sql為例:
對于select * from table where id =?這種場景,無論執(zhí)行多少次,都是冪等的。
update table set col =? where col2 = ?,也是冪等的
delete from table where id =? 也是冪等的
insert into table(id,name....) values (1,name,...),如果id是唯一主鍵,那么該操作也是冪等的。
冪等解決方案
token機制
1.服務端提供發(fā)送token接口,需要冪等的接口,就在執(zhí)行業(yè)務前,獲取token,服務器將token保存到redis中。
2.然后調(diào)用業(yè)務接口時,將token放在請求頭中。
3.服務器判斷token是否存在redis中,存在表示第一次請求,然后刪除token,繼續(xù)執(zhí)行業(yè)務。
4.如果判斷token不在redis中,就是重復操作,直接返回重復標記給client,就保證業(yè)務不被重復執(zhí)行。
危險性:
1.先刪除token還是后刪除token
先刪除,網(wǎng)絡閃斷等原因導致業(yè)務確實沒有執(zhí)行,然后觸發(fā)重試機制,由于防重設計,請求不能被執(zhí)行
后刪除,業(yè)務處理成功,但服務閃斷,出現(xiàn)超時,沒有刪除token,別人繼續(xù)重試,導致業(yè)務執(zhí)行多次。
最好的設計為先刪除token,如果業(yè)務調(diào)用失敗,重新獲取token再次請求。
2.token獲取、比較和刪除必須為原子性
redis.get(token)、redis.equals、redis.del(token),如果這些操作不是原子性的,高并發(fā)情況下,可能get到同樣數(shù)據(jù),判斷都成功,繼續(xù)業(yè)務
可以使用lua腳本保證redis操作的原子性
if redis.call('get',KEYS[1]) == ARGV[1]
then return redis.call('del',KEYS[1])
else return 0 end
鎖機制
悲觀鎖
select * from table where id = ? for update;
悲觀鎖使用時一般伴隨著事務一起使用,數(shù)據(jù)庫鎖定的時間可能會有點長。
注意:id必須時主鍵或者時唯一索引,不然會導致鎖表的結果。
樂觀鎖
主要適用于讀多寫少的場景。
更新場景:
例如:update table set col = ? ,version= version+1 where id=? and version =1
可以根據(jù)version版本號,操作的時候需要帶上version。
當?shù)谝淮螆?zhí)行后,version變成2后,再次執(zhí)行上述sql,where條件不成立,也保證了冪等性。
分布式鎖
多個機器同時處理相同數(shù)據(jù),我們可以加上分布式鎖(redis或者zookeeper等),同一時間,只有一個機器能拿到分布式鎖,執(zhí)行業(yè)務,處理完成后,釋放鎖,獲取到鎖的時候必須判斷該業(yè)務是否處理過,如果是,則不處理。
唯一約束
數(shù)據(jù)庫唯一約束
插入數(shù)據(jù),應該按照唯一索引進行插入,相同的唯一索引只能有一條,可以在數(shù)據(jù)庫中防止重復。但是要保證在同一個業(yè)務下發(fā)多次請求都生成全局唯一的主鍵。
分庫分表場景時,要保證相同請求落地到同一數(shù)據(jù)庫同一張表。
redis set集合防重復
計算數(shù)據(jù)的MD5,放入set集合中,每次處理,先看MD5是否已經(jīng)存在,存在則不處理。
防重表
專門新建一個數(shù)據(jù)表作為防重表。處理業(yè)務時,先將唯一索引(例如訂單號)插入防重表,在進行業(yè)務操作,并且在同一事務中。
全局請求唯一id
調(diào)用接口時,生成一個唯一id,redis將id存在set中,存在則處理過。
可以使用nginx設置每一個請求的唯一id。
proxy_set_header X-Request-Id $request_id
此外,我們通過$request_id 實現(xiàn)客戶端->網(wǎng)關服務器->微服務集群A->>微服務集群B.... 實現(xiàn)日志串聯(lián)。通過trace_id回顯,跟蹤每次調(diào)用路由。
總結
- 上一篇: Angulary应用依赖里的platfo
- 下一篇: Angular应用里的Template