天天看點

接口的幂等性

最近跟朋友聊起這個話題,想深入了解下,于是學習總結,記錄下來,此文章參考以下部落格綜合而來表示感謝:

http://blog.brucefeng.info/post/api-idempotent

http://825635381.iteye.com/blog/2276077

https://www.cnblogs.com/leechenxiang/p/6626629.html

1. 接口調用存在的問題

        現如今我們的系統大多拆分為分布式SOA,或者微服務,一套系統中包含了多個子系統服務,而一個子系統服務往往會去調用另一個服務,而服務調用服務無非就是使用RPC通信或者restful,既然是通信,那麼就有可能在伺服器處理完畢後傳回結果的時候挂掉,這個時候使用者端發現很久沒有反應,那麼就會多次點選按鈕,這樣請求有多次,那麼處理資料的結果是否要統一呢?那是肯定的!尤其在支付場景。

2. 什麼是接口幂等性

       接口幂等性就是使用者對于同一操作發起的一次請求或者多次請求的結果是一緻的,不會因為多次點選而産生了副作用。舉個最簡單的例子,那就是支付,使用者購買商品後支付,支付扣款成功,但是傳回結果的時候網絡異常,此時錢已經扣了,使用者再次點選按鈕,此時會進行第二次扣款,傳回結果成功,使用者查詢餘額返發現多扣錢了,流水記錄也變成了兩條...,這就沒有保證接口的幂等性

3. 什麼情況下需要保證接口的幂等性

   在增删改查4個操作中,尤為注意就是增加或者修改,

  A: 查詢操作

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

 B: 删除操作

    删除一次和多次删除都是把資料删除。(注意可能傳回結果不一樣,删除的資料不存在,傳回0,删除的資料多條,傳回結果多個,在不考慮傳回結果的情況下,删除操作也是具有幂等性的)

 C: 更新操作

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

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

   把表中id為XXX的記錄的A字段值增加1,這種操作就不是幂等的

 D: 新增操作

   增加在重複送出的場景下會出現幂等性問題,如以上的支付問題

4. 那麼如何設計接口才能做到幂等呢?

   常見的兩種實作方案:  1. 通過代碼邏輯判斷實作    2. 使用token機制實作      下面以支付系統為例,分别對接口的幂等性進行說明與實作

    A: 通過代碼邏輯判斷實作接口幂等性,隻能針對一些滿足判斷的邏輯實作,具有一定局限性

   使用者購買商品的訂單系統與支付系統;訂單系統負責記錄使用者的購買記錄已經訂單的流轉狀态(orderStatus),支付系統用于付款,提供如下接口,訂單系統與支付系統通過分布式網絡互動。

接口的幂等性

這種情況下,支付系統已經扣款,但是訂單系統因為網絡原因,沒有擷取到确切的結果,是以訂單系統需要重試。由上圖可見,支付系統并沒有做到接口的幂等性,訂單系統第一次調用和第二次調用,使用者分别被扣了兩次錢,不符合幂等性原則(同一個訂單,無論是調用了多少次,使用者都隻會扣款一次)。如果需要支援幂等性,付款接口需要修改為以下接口:

  boolean pay(int orderId,int accountId,BigDecimal amount)

通過orderId來标定訂單的唯一性,付款系統隻要檢測到訂單已經支付過,則第二次調用不會扣款而會直接傳回結果:

接口的幂等性

在不同的業務中不同接口需要有不同的幂等性,特别是在分布式系統中,因為網絡原因而未能得到确定的結果,往往需要支援接口幂等性。

随着分布式系統及微服務的普及,因為網絡原因而導緻調用系統未能擷取到确切的結果進而導緻重試,這就需要被調用系統具有幂等性。例如上文所闡述的支付系統,針對同一個訂單保證支付的幂等性,一旦訂單的支付狀态确定之後,以後的操作都會傳回相同的結果,對使用者的扣款也隻會有一次。這種接口的幂等性,簡化到資料層面的操作:

接口的幂等性

    當orderStatus 處于0,1兩種狀态時,對訂單執行0->1 的狀态流轉操作應該是具有幂等性的。這時候需要在執行update操作之前檢測orderStatus是否已經=1,如果已經=1則直接傳回true即可。

    但是如果此時orderStatus = 2,再進行訂單狀态0->1 時操作就無法成功,但是幂等性是針對同一個請求的,也就是針對同一個requestid保持幂等。

    這時候再執行

    接口會傳回失敗,系統沒有産生修改,如果再發一次,requestid是相同的,對系統同樣沒有産生修改。

 B: 使用token機制實作接口幂等性,通用性強的實作方法

     token機制實作步驟:

     1. 生成全局唯一的token,token放到redis或jvm記憶體,token會在頁面跳轉時擷取.存放到pageScope中,支付請求送出先擷取token

     2. 送出後背景校驗token,執行送出邏輯,送出成功同時删除token,生成新的token更新redis ,這樣當第一次送出後token更新了,頁面再次送出攜帶的token是已删除的token背景驗證會失敗不讓送出

     token特點:   要申請,一次有效性,可以限流

     注意: redis要用删除操作來判斷token,删除成功代表token校驗通過,如果用select+delete來校驗token,存在并發問題,不建議使用