接口幂等性详解
1.什么是接口冪等
接口冪等性就是用戶對同一操作發(fā)起了一次或多次請求的對數(shù)據(jù)的影響是一致不變的,不會因為多次的請求而產(chǎn)生副作用。
副作用:可以認為多次請求操作,每一次對數(shù)據(jù)狀態(tài)都會產(chǎn)生影響 。
注意這里并沒有要求接口返回結(jié)果是一致的。
例如:update order set moeny = 100 where orderId = 2029282312
該操作無論執(zhí)行多少次,對數(shù)據(jù)的影響都是一致的,不變的。
接口不做冪等處理會怎樣?
支付場景:用戶購買商品后,發(fā)起支付操作,支付系統(tǒng)處理支付成功后,由于網(wǎng)絡原因沒有及時返回給用戶結(jié)果,其實這個時候訂單已經(jīng)扣過款,相應的支付流水也都已經(jīng)生成,這個時候用戶又點擊支付操作,此時會進行第二次扣款,扣款成功后返回給用戶。用戶去查看支付訂單和流水會發(fā)現(xiàn)自己支付兩次,完蛋了要用戶被投訴了,這就是接口沒有處理冪等造成的。
2.什么情況需要處理接口冪等性問題?
2.1 select 天然自帶冪等性。
每次查詢對數(shù)據(jù)都不會產(chǎn)生副作用。
2.2 insert 當我們重復插入數(shù)據(jù)的時候,會出現(xiàn)什么情況 ?
第一種情況:自增主鍵,沒有冪等性。
eg:insert into product_info (id,name,type,price,tm)
執(zhí)行多次,會新增多條記錄。對結(jié)果集產(chǎn)生了副作用。
第二種情況:業(yè)務主鍵,具有冪等。
eg:insert into product_info (orderId,name,type,price,tm) orderId 為主鍵唯一
無論該sql執(zhí)行多少次,對結(jié)果集產(chǎn)生的效果都是一樣只增加了一條數(shù)據(jù)。
2.3 delete 是否具有冪等性?
第一種情況:絕對刪除,具有冪等性。
eg;delete from order where id = 3 。
無論該sql執(zhí)行多少次,對結(jié)果集產(chǎn)生的效果都是一樣只刪除了一條數(shù)據(jù)。
第二種情況: 相對刪除,不具有冪等性。
eg:delete from order where id > 23 .
該操作每執(zhí)行一次,對結(jié)果集產(chǎn)生的結(jié)果,可能都不一樣,同一操作多次執(zhí)行對數(shù)據(jù)產(chǎn)生了副作用。
2.4 update 猜一猜是否具有天熱冪等性?
第一種情況:絕對更新,具有冪等性。
eg:update good set stock= 586 where goodId = 10;
該操作無論執(zhí)行多少次操作對結(jié)果的影響都是一樣。
第二種情況:相對更新,不具有冪等性。
eg:update good set stock = stock+10 where goodid= 10 ;
每次執(zhí)行該操作庫存數(shù)量都會加10,所以不具備冪等操作。
總結(jié):以上都是基于單庫,單表的操作冪等性的分析,其實在具體業(yè)務當中,可能要設計多個表,多個庫,甚至跨服務操作。比如分布式系統(tǒng)中,我們一個接口,可能需要調(diào)用多個服務來完成任務。那么這種情況,如何保證接口的冪等性呢?
接口冪等性解決方案
前言:接口冪等處理要根據(jù)具體業(yè)務來判斷怎么處理,以下會舉例來闡述接口冪等處理解決方案。
1.token+redis 機制
比如訂單支付場景:
????????該支付分為兩個步驟:
????????????????1.1 獲取全局唯一token
???????????????????????接口處理生成唯一標識(token) 存儲到redis中,并返回給調(diào)用客戶端。
????????????????1.2 發(fā)起支付操作并附帶token
????????????????????接口處理:
??????????????????? 1.2.1 獲得分布式鎖(處理并發(fā)情況)
??????????????????? 1.2.2 判斷redis中是否存在token
??????????????????? 1.2.3 存在 執(zhí)行支付業(yè)務邏輯,否則返回該訂單已經(jīng)支付
??????????????????? 1.2.4 釋放分布式鎖
思考:為什么要加分布式鎖?
原因1:在高并發(fā)請求中 ,token判斷是否存在是非線程安全的,所以要加分布式鎖來保證 該條件的判斷為線程安全
????????注釋:也可redis用刪除操作來判斷token,刪除成功代表token校驗通過 這個刪除是原子操作的
原因2:在支付業(yè)務中,判斷支付訂單是否已經(jīng)存在,存在說明該訂單已經(jīng)支付過了,不存在就執(zhí)行扣款操作,如果相同操作并發(fā)兩個請求來到判斷條件可能兩個請求都能判斷支付訂單不存在,造成重復扣款。 所以也要加分布式鎖保證線程的安全。
2.CAS 保證接口冪等性
2.1 狀態(tài)機制冪等(狀態(tài)不可逆)
針對更新操作:
例如 電商訂單,訂單支付狀態(tài) 0 待支付 , 1 支付中 , 3 支付成功 4 支付失敗。
update order set status = 1 where status =0 and orderId = “201251487987”
該sql語句利用狀態(tài)CAS 保證該操作的冪等。
????????eg:比如要進行訂單支付,上來先用CAS更新訂單狀態(tài),
????????????返回影響說為1 代表修改成功,可以支付,繼續(xù)執(zhí)行支付業(yè)務代碼
?? ??????????返回影響數(shù) 0 代表修改失敗,該訂單已經(jīng)不是待支付訂單了。
注釋:實際這里是利用CAS原理
3 樂觀鎖實現(xiàn)冪等
背景由來:
????????????為什么要有冪等這種場景?因為在大的系統(tǒng)中,都是分布式部署,如:訂單業(yè)務 和 庫存業(yè)務有可能都是獨立部署的,都是單獨的服務。用戶下訂單,會調(diào)用到訂單服務和庫存服務。
比如:訂單系統(tǒng):
訂單服務 —> 庫存服務 (PRC遠程調(diào)用(服務接口))
????????因為分布式部署,很有可能在調(diào)用庫存服務時,因為網(wǎng)絡等原因,訂單服務調(diào)用失敗,但其實庫存服務已經(jīng)處理完成,只是返回給訂單服務處理結(jié)果時出現(xiàn)了異常。這個時候一般系統(tǒng)會作補償方案,也就是訂單服務再此放起庫存服務的調(diào)用,庫存減1
update t_goods set count = count -1 where good_id=2
??????????這樣就出現(xiàn)了問題,其實上一次調(diào)用已經(jīng)減了1,只是訂單服務沒有收到處理結(jié)果。現(xiàn)在又調(diào)用一次,又要減1,這樣就不符合業(yè)務了,多扣了。
????????冪等這個概念就是,不管庫存服務在相同條件下調(diào)用幾次,處理結(jié)果都一樣。這樣才能保證補償方案的可行性。
樂觀鎖方案
????借鑒數(shù)據(jù)庫的樂觀鎖機制,如:
???? update t_goods set count = count -1 , version = version + 1 where good_id=2 and version = 1
4 防重表
1.利用數(shù)據(jù)庫建一張防重表(加唯一索引)
???? 比如訂單支付,
????????反正重復支付:訂單號插入防重表 成功 執(zhí)行支付業(yè)務邏輯,失敗說明已經(jīng)支付過。
防重表支付成功是否要刪除:
1.可定期清除數(shù)據(jù)
2.也可結(jié)合 訂單狀態(tài) ,在支付前查詢訂單狀態(tài)為待支付 執(zhí)行支付操作 ,操作后刪除訂單號 若 第二個請求插入防重表成功,但是這個時候查詢訂單狀態(tài)失敗。
(實際這個防重表就是實現(xiàn)了分布式鎖)
5. 緩存隊列
將請求放入隊列,后續(xù)使用異步任務處理隊列中的數(shù)據(jù),過濾掉重復的消息。 和防止重復消費道理是一樣。
總結(jié)
- 上一篇: c语言 在有n个元素的数组s中查找书名为
- 下一篇: Dx bad class file ma