batchsize一定是2的幂_支付接口的幂等性设计
1. 什么是冪等性?
在數學中的冪等性定義:
在某二元運算下,冪等元素是指被自己重復運算(或對于函數是為復合)的結果等于它自己的元素。例如,乘法下唯一兩個冪等實數為0和1。 即 s * s = s
某一元運算為冪等時,其作用在任一元素兩次后會和其作用一次的結果相同。例如,高斯符號便是冪等的,即f(f(x)) = f(x)。
在HTTP/1.1規范中的冪等性定義:
如果一個請求方法在服務器上多次執行的預期影響與它只執行一次相同,那么這個請求方法就被認為具有冪等性。
HTTP的冪等性指的是一次和多次請求某一個資源應該具有相同的副作用。如通過PUT接口將數據的Status置為1,無論是第一次執行還是多次執行,獲取到的結果應該是相同的,即執行完成之后Status =1。
2. 何種接口提供冪等性
2.1 HTTP支持冪等性的接口
在HTTP規范中定義GET,PUT和DELETE方法應該具有冪等性。
GET方法
GET方法是向服務器查詢,不會對系統產生副作用,具有冪等性(不代表每次請求都是相同的結果)
PUT方法
也就是說PUT方法首先判斷系統中是否有相關的記錄,如果有記錄則更新該記錄,如果沒有則新增記錄。
DELETE 方法
DELETE方法是刪除服務器上的相關記錄。
2.2 實際業務
比如現在有這樣一個系統,用戶購買商品的訂單系統與支付系統;訂單系統負責記錄用戶的購買記錄已經訂單的流轉狀態(orderStatus),支付系統用于付款,提供
boolean pay(int accountid,BigDecimal amount) //用于付款,扣除用戶的接口,訂單系統與支付系統通過分布式網絡交互。
這種情況下,支付系統已經扣款,但是訂單系統因為網絡原因,沒有獲取到確切的結果,因此訂單系統需要重試。 由上圖可見,支付系統并沒有做到接口的冪等性,訂單系統第一次調用和第二次調用,用戶分別被扣了兩次錢,不符合冪等性原則(同一個訂單,無論是調用了多少次,用戶都只會扣款一次)。 如果需要支持冪等性,付款接口需要修改為以下接口:
boolean pay(int orderId,int accountId,BigDecimal amount)通過orderId來標定訂單的唯一性,付款系統只要檢測到訂單已經支付過,則第二次調用不會扣款而會直接返回結果:
在不同的業務中不同接口需要有不同的冪等性,特別是在分布式系統中,因為網絡原因而未能得到確定的結果,往往需要支持接口冪等性。
3. 分布式系統接口冪等性
隨著分布式系統及微服務的普及,因為網絡原因而導致調用系統未能獲取到確切的結果從而導致重試,這就需要被調用系統具有冪等性。 例如上文所闡述的支付系統,針對同一個訂單保證支付的冪等性,一旦訂單的支付狀態確定之后,以后的操作都會返回相同的結果,對用戶的扣款也只會有一次。這種接口的冪等性,簡化到數據層面的操作:
update userAmount set amount = amount - 'value' ,paystatus = 'paid' where orderId= 'orderid' and paystatus = 'unpay'其中value是用戶要減少的訂單,paystatus代表支付狀態,paid代表已經支付,unpay代表未支付,orderid是訂單號。 在上文中提到的訂單系統,訂單具有自己的狀態(orderStatus),訂單狀態存在一定的流轉。
訂單首先有提交(0),付款中(1),付款成功(2),付款失敗(3),簡化之后其流轉路徑如圖:
當orderStatus = 1 時,其前置狀態只能是0,也就是說將orderStatus由0->1 是需要冪等性的
update Order set orderStatus = 1 where OrderId = 'orderid' and orderStatus = 0當orderStatus 處于0,1兩種狀態時,對訂單執行0->1 的狀態流轉操作應該是具有冪等性的。 這時候需要在執行update操作之前檢測orderStatus是否已經=1,如果已經=1則直接返回true即可。
但是如果此時orderStatus = 2,再進行訂單狀態0->1 時操作就無法成功,但是冪等性是針對同一個請求的,也就是針對同一個requestid保持冪等。
這時候再執行
update Order set orderStatus = 1 where OrderId = 'orderid' and orderStatus = 0接口會返回失敗,系統沒有產生修改,如果再發一次,requestid是相同的,對系統同樣沒有產生修改。
另外還有其他解決方案:
全局唯一ID
如果使用全局唯一ID,就是根據業務的操作和內容生成一個全局ID,在執行操作前先根據這個全局唯一ID是否存在,來判斷這個操作是否已經執行。如果不存在則把全局ID,存儲到存儲系統中,比如數據庫、redis等。如果存在則表示該方法已經執行。
從工程的角度來說,使用全局ID做冪等可以作為一個業務的基礎的微服務存在,在很多的微服務中都會用到這樣的服務,在每個微服務中都完成這樣的功能,會存在工作量重復。另外打造一個高可靠的冪等服務還需要考慮很多問題,比如一臺機器雖然把全局ID先寫入了存儲,但是在寫入之后掛了,這就需要引入全局ID的超時機制。
使用全局唯一ID是一個通用方案,可以支持插入、更新、刪除業務操作。但是這個方案看起來很美但是實現起來比較麻煩,下面的方案適用于特定的場景,但是實現起來比較簡單。
去重表
這種方法適用于在業務中有唯一標的插入場景中,比如在以上的支付場景中,如果一個訂單只會支付一次,所以訂單ID可以作為唯一標識。這時,我們就可以建一張去重表,并且把唯一標識作為唯一索引,在我們實現時,把創建支付單據和寫入去去重表,放在一個事務中,如果重復創建,數據庫會拋出唯一約束異常,操作就會回滾。
插入或更新
這種方法插入并且有唯一索引的情況,比如我們要關聯商品品類,其中商品的ID和品類的ID可以構成唯一索引,并且在數據表中也增加了唯一索引。這時就可以使用InsertOrUpdate操作。在mysql數據庫中如下:
insert into goods_category (goods_id,category_id,create_time,update_time)values(#{goodsId},#{categoryId},now(),now())on DUPLICATE KEY UPDATEupdate_time=now()多版本控制
這種方法適合在更新的場景中,比如我們要更新商品的名字,這時我們就可以在更新的接口中增加一個版本號,來做冪等
boolean updateGoodsName(int id,String newName,int version);在實現時可以如下
update goods set name=#{newName},version=#{version} where id=#{id} and version狀態機控制
這種方法適合在有狀態機流轉的情況下,比如就會訂單的創建和付款,訂單的付款肯定是在之前,這時我們可以通過在設計狀態字段時,使用int類型,并且通過值類型的大小來做冪等,比如訂單的創建為0,付款成功為100。付款失敗為99
在做狀態機更新時,我們就這可以這樣控制
update `order` set status=#{status} where id=#{id} and status以上就是保證接口冪等性的一些方法
總結
以上是生活随笔為你收集整理的batchsize一定是2的幂_支付接口的幂等性设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 施工日志管理软件app_工程人必备的手机
- 下一篇: irobot扫地机器人 电压_iRobo