天天看點

Java進階知識點6:接口幂等性1.幂等地定義2.需要幂等的場景3.“天然”的幂等和需要“人工”的幂等4.幂等實作方法5.總結

1.幂等地定義

1.1數學定義

在數學裡,幂等有兩種主要的定義:

  • 在某二進制運算下,幂等元素是指被自己重複運算(或對于函數是為複合)的結果等于它自己的元素。如,乘法運算下,0和1符合的自乘運算符和幂等,即s*s=s
  • 某一進制運算為幂等的時,其作用在任一進制素兩次後會和其作用一次的結果相同。例如,高斯符号便是幂等的,即f(f(x))=f(x)

1.2 計算機中的幂等

在計算機中,表示對同一個過程應用相同的參數多次和應用一次産生的效果是一樣,這樣的過程即被稱為滿足幂等性

幂等:

update test_user set user_age = 25 where user_id = 2 ,這中情況無論執行多少次,結果都不受影響,是以是幂等的。

非幂等:

update test_user set user_times = user_times + 1 where user_id = 2, 這樣的更新語句每執行一次,結果都會不一樣,是以是非幂等的。

1.2.1Http規範定義

在HTTP/1.1規範中幂等性的定義是:

A request method is considered "idempotent" if the intended  
    effect on the server of multiple identical requests with that     
    method is the same as the effect for a single such request. Of 
    the request methods defined by this specification, PUT, 
    DELETE, and safe request methods are idempotent.
           

即:

一個請求方法,如果被請求多次和被請求一次效果相同,被認為是幂等的,比如PUT、DELETE和其他安全的請求方法都是幂等的。

1.2.2 微服務場景中幂等

由于微服務的普及,原有的單體應用,被設計成不同的功能子產品作為服務,獨立部署在不同的實體環境中。要完成一個完整的業務流程,就需要在多個微服務中間進行調用,而調用的過程當然是經由網絡來完成的。

是以,網絡通信的不确定性因素,比如網絡的抖動,對端微服務的異常,可能會導緻微服務間調用,産生逾時的現象。為保證業務流程的順利完成,調用過程必須建立重試機制。

然而,一旦建立了重試機制,那就可能會将同一個請求發送多次,導緻接受方重複消費,多次執行相同操作,進而可能産生錯誤的資料。為避免這個問題,必須保證可能産生錯誤資料的接口(方法),請求一次和請求多次的效果相同,即幂等。

2.需要幂等的場景

可能會發生重複請求或消費的場景,在微服務架構中是随處可見的。以下是筆者梳理的幾個常見場景:

  • 網絡波動:

    因網絡波動,可能會引起重複請求

  • 分布式消息消費:

    任務釋出後,使用分布式消息服務來進行消費,參考【消息總線真的能保證幂等?】

  • 使用者重複操作:

    使用者在使用産品時,可能會誤操作而觸發多筆交易,或者因為長時間沒有響應,而有意觸發多筆交易。

  • 未關閉的重試機制:

    技術人員人為的錯誤,因開發人員、測試人員或運維人員沒有檢查出來,而開啟的重試機制(如Nginx重試、RPC通信重試或業務層重試等)

3.“天然”的幂等和需要“人工”的幂等

3.1CRUD分析

CRUD是指在做計算處理時的[增加](Create)、讀取(Read)、更新(Update)和删除(Delete)幾個單詞的首字母簡寫。主要被用在描述軟體系統中資料庫或者持久層的基本操作功能。

是以CRUD角度分析幂等性,是從操作目的層面來看問題的:

操作 幂等性
新增類請求(C) 資料庫自增主鍵,不具備幂等性
查詢類動作(R) 重複查詢不會産生或變更新的資料,是以查詢是天然具備幂等性
基于主鍵的計算式更新(U) 不具備幂等性,即:UPDATE goods SET number=number-1 WHERE id=1
基于主鍵的非計算式更新(U) 具備幂等性,即:UPDATE goods SET number=newNumber WHERE id=1
基于條件查詢的更新(U) 不一定具有幂等性(需要根據實際情況進行分析判斷)
基于主建的删除(D) 具備幂等性
業務層面都是邏輯删除(即Update操作)(U) 不具備幂等性

3.2 HTTP方法分析

按照restful規範定義的接口,使用http方法,應該嚴格遵循http方法語義:

方法 幂等性 對應CRUD操作
POST 不安全且不幂等 C
GET 安全且幂等 R
PUT 不安全但幂等 U
DELETE 不安全但幂等 D

3.3 “天然”的幂等

GET,PUT,DELETE都是幂等操作,而POST不是,以下進行分析:

首先GET請求很好了解,對資源做查詢多次,此實作的結果都是一樣的。

PUT請求的幂等性可以這樣了解,将A修改為B,它第一次請求值變為了B,再進行多次此操作,最終的結果還是B,與一次執行的結果是一樣的,即屬于CURD中所說的基于主鍵的非計算式更新,是以PUT是幂等操作。

同理可以了解DELETE操作,第一次将資源删除後,後面多次進行此删除請求,最終結果是一樣的,将資源删除掉了。

3.4需要“人工”的幂等

POST不是幂等操作,因為一次請求添加一份新資源,二次請求則添加了兩份新資源,多次請求會産生不同的結果,是以POST不是幂等操作。

如果需要在POST方法的接口實作幂等,需要人為加上幂等的機制。

下面我們來說說,幂等地實作方法。

4.幂等實作方法

4.1 全局唯一ID

如果使用全局唯一ID,就是根據業務的操作和内容生成一個全局ID,在執行操作前先根據這個全局唯一ID是否存在,來判斷這個操作是否已經執行。如果不存在則把全局ID,存儲到存儲系統中,比如資料庫、Redis等。如果存在則表示該方法已經執行。

使用全局唯一ID是一個通用方案,可以支援插入、更新、删除業務操作。比較适用于有限制導入導出操作,比如:希望一個賬号在同一時間内隻能導入或導出一次資料,這個時候就可以在導入前根據賬号建立一個對賬号唯一的key值放入redis中,執行業務方法時判斷是否存在,最後執行成功或失敗後删除redis的Key.

但是這個方案看起來很美但是實作起來比較麻煩,下面的方案适用于特定的場景,但是實作起來比較簡單。

4.2 去重表

這種方法适用于在業務中有唯一标的插入場景中.

比如在以上的支付場景中,如果一個訂單隻會支付一次,是以訂單ID可以作為唯一辨別。這時,我們就可以建一張去重表,并且把唯一辨別作為唯一索引,用以記錄訂單支付資訊,在我們實作時,把建立支付單據和寫入去去重表,放在一個事務中,如果重複建立,資料庫會抛出唯一限制異常,操作就會復原。這個方法其實也是用到唯一ID,與上面全局唯一ID不同的是,他是針對具體單個業務流程的,實作起來相對簡單。

4.3 插入或更新

這種方法插入并且有唯一索引的情況,比如我們要關聯商品品類,其中商品的ID和品類的ID可以構成唯一索引,并且在資料表中也增加了唯一索引。這時就可以使用InsertOrUpdate操作。在mysql資料庫中如下:

insert into goods_category    
(goods_id,category_id,create_time,update_time) 
  values(#{goodsId},#{categoryId},now(),now()) 
on DUPLICATE KEY UPDATE update_time=now()
           

4.4 多版本控制

這種方法适合在更新的場景中,比如我們要更新商品的名字,這時我們就可以在更新的接口中增加一個版本号,來做幂等:

boolean updateGoodsName(int id,String newName,int version);
           

在實作時可以如下:

update goods set name=#{newName},version=#{version} where  
id=#{id} and version<${version}
           

4.5 狀态機控制

這種方法适合在有狀态機流轉的情況下,比如訂單的建立和付款,訂單的付款肯定是在之前,這時我們可以通過在設計狀态字段時,使用int類型,并且通過值類型的大小來做幂等,比如訂單的建立為0,付款成功為100,付款失敗為99。在做狀态機更新時,我們就這可以這樣控制:

update goods_order set status=#{status} where id=#{id} and   
status<#{status}
           

以上就是保證接口幂等性的一些方法。

5.總結

幂等性設計不能脫離業務來讨論,一般情況下,去重表同時也是業務資料表,而針對分布式的去重ID,可以參考以下幾種方式:

  • UUID
  • Snowflake
  • 資料庫自增ID
  • 業務本身的唯一限制
  • 業務字段+時間戳拼接

-------------------------------------------------------------------

作者:撫劍聽琴

連結:https://www.jianshu.com/p/6707cbc4b294

來源:簡書

簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。