接口調用存在的問題html
現現在咱們的系統大多拆分爲分佈式SOA,或者微服務,一套系統中包含了多個子系統服務,而一個子系統服務每每會去調用另外一個服務,而服務調用服務無非就是使用RPC通訊或者restful,既然是通訊,那麼就有可能在服務器處理完畢後返回結果的時候掛掉,這個時候用戶端發現好久沒有反應,那麼就會屢次點擊按鈕,這樣請求有屢次,那麼處理數據的結果是否要統一呢?那是確定的!尤爲在支付場景。
什麼是接口冪等性redis
接口冪等性就是用戶對於同一操做發起的一次請求或者屢次請求的結果是一致的,不會由於屢次點擊而產生了反作用。舉個最簡單的例子,那就是支付,用戶購買商品後支付,支付扣款成功,可是返回結果的時候網絡異常,此時錢已經扣了,用戶再次點擊按鈕,此時會進行第二次扣款,返回結果成功,用戶查詢餘額返發現多扣錢了,流水記錄也變成了兩條...,這就沒有保證接口的冪等性
什麼狀況下須要保證接口的冪等性服務器
在增刪改查4個操做中,尤其注意就是增長或者修改,restful
A: 查詢操做網絡
查詢對於結果是不會有改變的,查詢一次和查詢屢次,在數據不變的狀況下,查詢結果是同樣的。select是自然的冪等操做
B: 刪除操做併發
刪除一次和屢次刪除都是把數據刪除。(注意可能返回結果不同,刪除的數據不存在,返回0,刪除的數據多條,返回結果多個,在不考慮返回結果的狀況下,刪除操做也是具備冪等性的)
C: 更新操做jvm
修改在大多場景下結果同樣,可是若是是增量修改是須要保證冪等性的,以下例子:分佈式
把表中id爲XXX的記錄的A字段值設置爲1,這種操做無論執行多少次都是冪等的
把表中id爲XXX的記錄的A字段值增長1,這種操做就不是冪等的微服務
D: 新增操做設計
增長在重複提交的場景下會出現冪等性問題,如以上的支付問題
那麼如何設計接口才能作到冪等呢?
常見的兩種實現方案: 1. 經過代碼邏輯判斷實現 2. 使用token機制實現 下面以支付系統爲例,分別對接口的冪等性進行說明與實現
A: 經過代碼邏輯判斷實現接口冪等性,只能針對一些知足判斷的邏輯實現,具備必定侷限性
用戶購買商品的訂單系統與支付系統;訂單系統負責記錄用戶的購買記錄已經訂單的流轉狀態(orderStatus),支付系統用於付款,提供以下接口,訂單系統與支付系統經過分佈式網絡交互。
boolean pay(int accountid,BigDecimal amount) //用於付款,扣除用戶的
這種狀況下,支付系統已經扣款,可是訂單系統由於網絡緣由,沒有獲取到確切的結果,所以訂單系統須要重試。由上圖可見,支付系統並無作到接口的冪等性,訂單系統第一次調用和第二次調用,用戶分別被扣了兩次錢,不符合冪等性原則(同一個訂單,不管是調用了多少次,用戶都只會扣款一次)。若是須要支持冪等性,付款接口須要修改成如下接口:
boolean pay(int orderId,int accountId,BigDecimal amount)
經過orderId來標定訂單的惟一性,付款系統只要檢測到訂單已經支付過,則第二次調用不會扣款而會直接返回結果:
在不一樣的業務中不一樣接口須要有不一樣的冪等性,特別是在分佈式系統中,由於網絡緣由而未能獲得肯定的結果,每每須要支持接口冪等性。
隨着分佈式系統及微服務的普及,由於網絡緣由而致使調用系統未能獲取到確切的結果從而致使重試,這就須要被調用系統具備冪等性。例如上文所闡述的支付系統,針對同一個訂單保證支付的冪等性,一旦訂單的支付狀態肯定以後,之後的操做都會返回相同的結果,對用戶的扣款也只會有一次。這種接口的冪等性,簡化到數據層面的操做:
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是相同的,對系統一樣沒有產生修改。
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,存在併發問題,不建議使用