天天看點

面試題:接口幂等性是什麼?如何設計?

作者:java小悠

接口幂等-幂等性-接口的幂等性-分布式幂等性-如何保證幂等-幂等性實作方案-去重表-下單幂等-支付幂等-扣還庫存幂等

什麼是接口幂等?

在計算機中程式設計中,一個幂等操作的特點是其任意多次執行所産生的影響均與第一次執行的影響相同。

接口的幂等性實際上就是接口可重複調用,在調用方多次調用的情況下,接口最終得到的結果是一緻的。有些接口可以天然的實作幂等性,比如查詢接口,對于查詢來說,你查詢一次和兩次,對于系統來說,沒有任何影響,查出的結果也是一樣。

為什麼接口需要幂等性設計

該問題等同于 為什麼會重複調用?

前端重複送出表單

在填寫一些表格時候,使用者填寫完成送出,很多時候會因網絡波動沒有及時對使用者做出送出成功響應,緻使使用者認為沒有成功送出,然後一直點送出按鈕,這時就會發生重複送出表單請求。

黑客惡意攻擊

例如在實作使用者投票這種功能時,如果黑客針對一個使用者進行重複送出投票,這樣會導緻接口接收到使用者重複送出的投票資訊,這樣會使投票結果與事實嚴重不符。

接口逾時重複送出

大部分RPC架構[比如Dubbo],為了防止網絡波動逾時等造成的請求失敗,都會添加重試機制,導緻一個請求送出多次。

消息重複消費

當使用 MQ 消息中間件時候,如果Consumer消費逾時或者producer發送了消息但由于網絡原因未收到ACK導緻消息重發,都會導緻重複消費。

哪些接口需要幂等?

幂等性的實作與判斷需要消耗一定的資源,是以不應該給每個接口都增加幂等性判斷,要根據實際的業務情況和操作類型來進行區分。例如,我們在進行查詢操作和删除操作時就無須進行幂等性判斷。

查詢操作查一次和查多次的結果都是一緻的,是以我們無須進行幂等性判斷。删除操作也是一樣,删除一次和删除多次都是把相關的資料進行删除(這裡的删除指的是條件删除而不是删除所有資料),是以也無須進行幂等性判斷。

是以到底哪些接口需要幂等?關于這個問題需要從具體業務出發,但是也有規律可循如下表:

面試題:接口幂等性是什麼?如何設計?

如何實作幂等

前端攔截

前端攔截是指通過 Web 站點的頁面進行請求攔截,比如在使用者點選完“送出”按鈕後,我們可以把按鈕設定為不可用或者隐藏狀态,避免使用者重複點選。

該方法可以解決使用者誤操作送出兩次表單所産生的重複送出問題。但前端攔截有一個緻命的問題,如果是懂行的程式員或者黑客可以直接繞過頁面的 JS 執行,直接模拟請求後端的接口,這樣的話,我們前端的這些攔截就不能生效了。是以除了前端攔截一部分正常的誤操作之外,後端的驗證必不可少。

資料庫唯一索引實作

資料庫唯一索引實作方案一般隻能适用于執行插入操作的過程。

面試題:接口幂等性是什麼?如何設計?

具體流程步驟:

  • 建立一張去重表,其中某個字段需要建立唯一索引
  • 用戶端去請求服務端,服務端會将這次請求的一些資訊插入這張去重表中
  • 因為表中某個字段帶有唯一索引,如果插入成功,證明表中沒有這次請求的資訊,則執行後續的業務邏輯
  • 如果插入失敗,則代表已經執行過目前請求,直接傳回

資料庫樂觀鎖實作

資料庫樂觀鎖方案一般隻能适用于執行更新操作的過程,我們可以提前在對應的資料表中多添加一個字段,充當目前資料的版本辨別。

這樣每次對該資料庫該表的這條資料執行更新時,都會将該版本辨別作為一個條件,值為上次待更新資料中的版本辨別的值。

具體流程步驟:

  • 用戶端帶着version字段請求服務端
  • 服務端執行update的時候需要給version+1,并且需要加version的更新條件如下SQL
update t set stock = stock - 1 , version = version + 1 where id = #{id} and version = #{version}
           

資料庫悲觀鎖實作

面試題:接口幂等性是什麼?如何設計?

具體流程步驟:

  • 用戶端通過業務id,通路服務端
  • 先查資料庫是否存在該業務id,查庫的時候需要加X鎖
  • 如果存在則說明是重複請求,不存在則進行業務邏輯處理

JVM鎖實作

JVM 鎖實作是指通過 JVM 提供的内置鎖如 Lock 或者是 synchronized 來實作幂等性。使用 JVM 鎖來實作幂等性的一般流程為:首先通過 Lock 對代碼段進行加鎖操作,然後再判斷此訂單是否已經被處理過,如果未處理則開啟事務執行訂單處理,處理完成之後送出事務并釋放鎖,執行流程如下圖所示:

面試題:接口幂等性是什麼?如何設計?

JVM 鎖存在的最大問題在于,它隻能應用于單機環境,因為 Lock 本身為單機鎖,是以它就不适應于分布式多機環境。

分布式鎖實作

分布式鎖實作解決JVM鎖實作單機鎖局限問題。

面試題:接口幂等性是什麼?如何設計?

具體流程步驟:

  • 用戶端先請求服務端,會拿到一個能代表這次請求業務的唯一字段
  • 将該字段以 SETNX 的方式存入 redis 中,并根據業務設定相應的逾時時間
  • 如果設定成功,證明這是第一次請求,則執行後續的業務邏輯
  • 如果設定失敗,則代表已經執行過目前請求,直接傳回

Token實作

面試題:接口幂等性是什麼?如何設計?

具體流程步驟:

  • 用戶端會先發送一個請求去擷取 token,服務端會生成一個全局唯一的 ID 作為 token 儲存在 redis 中,同時把這個 ID 傳回給用戶端
  • 用戶端第二次調用業務請求的時候必須攜帶這個 token
  • 服務端會校驗這個 token,如果校驗成功,則執行業務,并删除 redis 中的 token
  • 如果校驗失敗,說明 redis 中已經沒有對應的 token,則表示重複操作,直接傳回指定的結果給用戶端

注意:

  • 對 redis 中是否存在 token 以及删除的代碼邏輯建議用 Lua 腳本實作,保證原子性
  • 全局唯一 ID 可以用百度的 uid-generator、美團的 Leaf 去生成

總結

幂等性不但可以保證程式正常執行,還可以杜絕一些垃圾資料以及無效請求對系統資源的消耗。推薦使用分布式鎖來實作,這樣的解決方案更加通用。

原文連結:https://mp.weixin.qq.com/s/xU1gvO4fPyLp5uWqWX50SQ

繼續閱讀