冪等的業務概念
就是使用者對於同一操作發起的一次請求或者多次請求的結果是一致的,不會因為多次點選而產生了副作用。
在增刪改查4個操作中,尤為注意就是增加或者修改。
(1) 查詢對於結果是不會有改變的,
(2) 刪除只會進行一次,使用者多次點選產生的結果一樣,
(3) 修改在大多場景下結果一樣
(4) 增加在重複提交的場景下會出現
介面不做冪等處理會怎樣?
支付場景:使用者購買商品後,發起支付操作,支付系統處理支付成功後,由於網路原因沒有及時返回給使用者結果,其實這個時候訂單已經扣過款,相應的支付流水也都
已經生成,這個時候使用者又點選支付操作,此時會進行第二次扣款,扣款成功後返回給使用者。使用者去檢視支付訂單和流水會發現自己支付兩次,完蛋了要被使用者投訴
了,這就是介面沒有處理冪等造成的。
冪等場景
1、查詢,select * from user where xxx,不會對資料產生任何變化,具備冪等性
2、新增,insert into user(userid, name) values(1, ‘a’)
如 userid 為唯一主鍵,即重複操作上面的業務,只會插入一條使用者資料,具備冪等性
如 userid 不是主鍵,可以重複,那上面業務多次操作,資料都會新增多條,不具備冪等性
3、修改,區分直接賦值和計算賦值
直接賦值,update user set point = 20 where userid = 1,不管執行多少次,point都一樣,具備冪等性
計算賦值,update user set point = point + 20 where userid = 1,每次操作 point 資料都不一樣,不具備冪等性
4、刪除,delete from user where userid = 1,多次操作,結果一樣,具備冪等性
上面場景中,我們發現新增沒有唯一主鍵約束的資料,和修改計算賦值型操作都不具備冪等性
解決方案
1、token + redis機制
token + redis 的冪等方案,適用於絕大部分場景。
token機制的核心思想是為每一次操作生成一個唯一性的憑證,也就是token。一個token在操作的每一個階段只有一次執行權,一旦執行成功則儲存執行結果。對重複
的請求,返回同一個結果。token機制的應用十分廣泛。
public Response checkToken(HttpServletRequest request) { //從請求頭中獲取token String token=request。getHeader(“token”); if (StringUtils。isBlank(token)){ //如果請求頭token為空就從引數中獲取 token=request。getParameter(“token”); //如果都為空丟擲引數異常的錯誤 if (StringUtils。isBlank(token)){ throw new ServiceException(ResponseCode。ILLEGAL_ARGUMENT。getCode()。toString(),ResponseCode。ILLEGAL_ARGUMENT。getMsg()); } } //如果redis中不包含該token,說明token已經被刪除了,丟擲請求重複異常 if (!redisTemplate。hasKey(token)){ throw new ServiceException(ResponseCode。REPETITIVE_OPERATION。getCode()。toString(),ResponseCode。REPETITIVE_OPERATION。getMsg()); } //刪除token Boolean del=redisTemplate。delete(token); //如果刪除不成功(已經被其他請求刪除),丟擲請求重複異常 if (!del){ throw new ServiceException(ResponseCode。REPETITIVE_OPERATION。getCode()。toString(),ResponseCode。REPETITIVE_OPERATION。getMsg()); } return new Response(0,“校驗成功”,null); }
token機制:提交時客戶端先請求後端獲取token,服務端生成token先進行快取,然後返回給客戶端。接下來,客戶端帶著token來請求,服務端先進行token驗證,判
斷token是否存在,如果存在則處理接下來的業務流程,然後刪除token。如果不存在則提示客戶端重複操作。
主要的流程步驟如下:
客戶端先發送獲取token的請求,服務端會生成一個全域性唯一的ID儲存在redis中,同時把這個ID返回給客戶端。
客戶端呼叫業務請求的時候必須攜帶這個token,一般放在請求頭上。
服務端會校驗這個Token,如果校驗成功,則執行業務。
如果校驗失敗,則表示重複操作,直接返回指定的結果給客戶端。
透過以上的流程分析,唯一的重點就是這個全域性唯一ID如何生成,在分散式服務中往往都會有一個生成全域性ID的服務來保證ID的唯一性,但是工程量和實現難度比較
大,UUID的資料量相對有些大,此處陳某選擇的是雪花演算法生成全域性唯一ID
2、去重表機制
這個方案業務中要有唯一主鍵,這個去重表中只要一個欄位就行,設定唯一主鍵約束,當然根據業務自行新增其他欄位。
上面的主要流程就是 把唯一主鍵插入去重表,再進行業務操作,且他們在同一個事務中。這個保證了重複請求時,因為去重表有唯一約束,導致請求失敗,避免了冪
等問題。
這裡要注意的是,去重表和業務表應該在同一庫中,這樣就保證了在同一個事務,即使業務操作失敗了,也會把去重表的資料回滾。這個很好的保證了資料一致性。
這個方案也是比較常用的,去重表是跟業務無關的,很多業務可以共用同一個去重表,只要規劃好唯一主鍵就行了。
3。 快取佇列
將請求放入佇列,後續使用非同步任務處理佇列中的資料,過濾掉重複的訊息。 和防止重複消費道理是一樣。