1. 程式人生 > >冪等一二三

冪等一二三

最近開發提現相關的功能,需要考慮冪等性。向社群取了點經,把自己的實現方法寫下來。不會高談闊論什麼是冪等,冪等種類等高深的問題。只是簡單的寫下步驟,題目來源於此。

為什麼需要冪等

分散式環境裡面,網路不可控,計算機不可控,都有失敗風險。先給定兩個角色,server,client,client是請求發起者,server是請求接收者並處理業務邏輯。第一次請求任何地方失敗了就直接告訴客戶處理失敗,這沒有問題,但是可用性不好,體驗差。有什麼辦法提高?答案是有限次的重試,比如重試三次,第一次失敗,還有第二次或者第三次機會。

問題來了,server接受到的請求可能是0個,1個,2個,或者3個。server收到3個重複的提現請求,當然只能最多真的提現一次。冪等解決的就是這個問題,最多處理一次,At Most Once

。沒有重複發請求的場景,不要考慮冪等問題。另外請求發出,server端只是查詢,沒有副作用,說明呼叫安全,也無需考慮冪等。

冪等解決思路

提供請求的身份證號碼,或者叫請求的ID,不同的請求不同的ID,重複的請求共享相同的ID。做的第一件事情是要區別請求相同還是不同。區分開了請求的異同,接下來就是實施,實施的思路很簡單,根據請求ID檢視請求是否處理成功,成功就跳出處理流程,告知已處理,不成功就繼續處理。

具體步驟

  1. 提供ID生成服務。client可以呼叫該服務,獲取新的ID。
    這一步可以跳過,client自己生成符合規範的唯一ID可行。

    String getTransactionId()
    
  2. client端呼叫攜帶請求ID。

    String transactionId = transactionService.getTransactionId();
    
    while(int i< 3){
        Response response = invokeServer(transactionId, request);
        if(response.isSuccess()){
            break;
        }
        i++;
     }
    
  3. server端實現
    提現涉及金錢,比較敏感,資料庫事務必不可少。所以server端的邏輯依賴資料庫事務。

3.1 建ID池,用table表示

CREATE TABLE `transaction` (
    `transaction_id` VARCHAR(64) NOT NULL COMMENT '事務ID',
    PRIMARY KEY (`transaction_id`)
)

3.2 事務保證

@Transactional
public class BusinessServiceImpl inplements BusinessService{
    @Override
    public void doSomething(String transactionId, Request request){
        // 向transaction表計入transactionId
        transactionDao.insert(new Transaction(transactionId));
        // 處理具體的業務邏輯
        doBussiness(request);
    }
}
  • transaction表的插入與業務處理,放到同一個事務,要麼都成功,要麼都失敗。
  • 相同的transactionId,由於transaction用transactionId作為主鍵,所以transaction最多有一個插入成功。
    所以業務處理也最多隻有一次成功,搞定。

其它

誰發起請求,誰負責transactionId。
能夠接受成功處理0次,但是不接受處理大於1次。