日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

接口设计的幂等性考虑

發(fā)布時(shí)間:2024/1/17 编程问答 63 豆豆
生活随笔 收集整理的這篇文章主要介紹了 接口设计的幂等性考虑 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

分布式系統(tǒng)接口冪等性

1.冪等性定義

1.1 數(shù)學(xué)定義

在數(shù)學(xué)里,冪等有兩種主要的定義:- 在某二元運(yùn)算下,冪等元素是指被自己重復(fù)運(yùn)算(或?qū)τ诤瘮?shù)是為復(fù)合)的結(jié)果等于它自己的元素。例如,乘法下唯一兩個(gè)冪等實(shí)數(shù)為0和1。即 s *s = s- 某一元運(yùn)算為冪等的時(shí),其作用在任一元素兩次后會(huì)和其作用一次的結(jié)果相同。例如,高斯符號(hào)便是冪等的,即f(f(x)) = f(x)。

1.2 HTTP規(guī)范的定義

在HTTP/1.1規(guī)范中冪等性的定義是:

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.

HTTP的冪等性指的是一次和多次請(qǐng)求某一個(gè)資源應(yīng)該具有相同的副作用。如通過PUT接口將數(shù)據(jù)的Status置為1,無論是第一次執(zhí)行還是多次執(zhí)行,獲取到的結(jié)果應(yīng)該是相同的,即執(zhí)行完成之后Status =1。

2. 何種接口提供冪等性

2.1 HTTP支持冪等性的接口

在HTTP規(guī)范中定義GET,PUT和DELETE方法應(yīng)該具有冪等性。

  • GET方法

The GET method requests transfer of a current selected representation for the target resource,GET is the primary mechanism of information retrieval and the focus of almost all performance optimizations. Hence, when people speak of retrieving some identifiable information via HTTP, they are generally referring to making a GET request.

GET方法是向服務(wù)器查詢,不會(huì)對(duì)系統(tǒng)產(chǎn)生副作用,具有冪等性(不代表每次請(qǐng)求都是相同的結(jié)果)

  • PUT方法

The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload.

也就是說PUT方法首先判斷系統(tǒng)中是否有相關(guān)的記錄,如果有記錄則更新該記錄,如果沒有則新增記錄。

  • DELETE 方法

The DELETE method requests that the origin server remove the association between the target resource and its current functionality. In effect, this method is similar to the rm command in UNIX: it expresses a deletion operation on the URI mapping of the origin server rather than an expectation that the previously associated information be deleted.

DELETE方法是刪除服務(wù)器上的相關(guān)記錄。

2.2 實(shí)際業(yè)務(wù)

現(xiàn)在簡(jiǎn)化為這樣一個(gè)系統(tǒng),用戶購買商品的訂單系統(tǒng)與支付系統(tǒng);訂單系統(tǒng)負(fù)責(zé)記錄用戶的購買記錄已經(jīng)訂單的流轉(zhuǎn)狀態(tài)(orderStatus),支付系統(tǒng)用于付款,提供

boolean pay(int accountid,BigDecimal amount) //用于付款,扣除用戶的

接口,訂單系統(tǒng)與支付系統(tǒng)通過分布式網(wǎng)絡(luò)交互。


訂單冪等性

這種情況下,支付系統(tǒng)已經(jīng)扣款,但是訂單系統(tǒng)因?yàn)榫W(wǎng)絡(luò)原因,沒有獲取到確切的結(jié)果,因此訂單系統(tǒng)需要重試。由上圖可見,支付系統(tǒng)并沒有做到接口的冪等性,訂單系統(tǒng)第一次調(diào)用和第二次調(diào)用,用戶分別被扣了兩次錢,不符合冪等性原則(同一個(gè)訂單,無論是調(diào)用了多少次,用戶都只會(huì)扣款一次)。如果需要支持冪等性,付款接口需要修改為以下接口:

boolean pay(int orderId,int accountId,BigDecimal amount)

通過orderId來標(biāo)定訂單的唯一性,付款系統(tǒng)只要檢測(cè)到訂單已經(jīng)支付過,則第二次調(diào)用不會(huì)扣款而會(huì)直接返回結(jié)果:

訂單支持冪等

在不同的業(yè)務(wù)中不同接口需要有不同的冪等性,特別是在分布式系統(tǒng)中,因?yàn)榫W(wǎng)絡(luò)原因而未能得到確定的結(jié)果,往往需要支持接口冪等性。

3.分布式系統(tǒng)接口冪等性

隨著分布式系統(tǒng)及微服務(wù)的普及,因?yàn)榫W(wǎng)絡(luò)原因而導(dǎo)致調(diào)用系統(tǒng)未能獲取到確切的結(jié)果從而導(dǎo)致重試,這就需要被調(diào)用系統(tǒng)具有冪等性。例如上文所闡述的支付系統(tǒng),針對(duì)同一個(gè)訂單保證支付的冪等性,一旦訂單的支付狀態(tài)確定之后,以后的操作都會(huì)返回相同的結(jié)果,對(duì)用戶的扣款也只會(huì)有一次。這種接口的冪等性,簡(jiǎn)化到數(shù)據(jù)層面的操作:

update userAmount set amount = 'value' ,paystatus = 'paid' where orderId= 'orderid' and paystatus = 'unpay'

其中value是用戶要減少的訂單,paystatus代表支付狀態(tài),paid代表已經(jīng)支付,unpay代表未支付,orderid是訂單號(hào)。在上文中提到的訂單系統(tǒng),訂單具有自己的狀態(tài)(orderStatus),訂單狀態(tài)存在一定的流轉(zhuǎn)。訂單首先有提交(0),付款中(1),付款成功(2),付款失敗(3),簡(jiǎn)化之后其流轉(zhuǎn)路徑如圖:


訂單狀態(tài)流轉(zhuǎn)的冪等性

當(dāng)orderStatus = 1 時(shí),其前置狀態(tài)只能是0,也就是說將orderStatus由0->1 是需要冪等性的

update Order set orderStatus = 1 where OrderId = 'orderid' and orderStatus = 0

當(dāng)orderStatus 處于0,1兩種狀態(tài)時(shí),對(duì)訂單執(zhí)行0->1 的狀態(tài)流轉(zhuǎn)操作應(yīng)該是具有冪等性的。這時(shí)候需要在執(zhí)行update操作之前檢測(cè)orderStatus是否已經(jīng)=1,如果已經(jīng)=1則直接返回true即可。

但是如果此時(shí)orderStatus = 2,再進(jìn)行訂單狀態(tài)0->1 時(shí)操作就無法成功,但是冪等性是針對(duì)同一個(gè)請(qǐng)求的,也就是針對(duì)同一個(gè)requestid保持冪等。

這時(shí)候再執(zhí)行

update Order set orderStatus = 1 where OrderId = 'orderid' and orderStatus = 0

接口會(huì)返回失敗,系統(tǒng)沒有產(chǎn)生修改,如果再發(fā)一次,requestid是相同的,對(duì)系統(tǒng)同樣沒有產(chǎn)生修改。

4.解決方案

在微服務(wù)架構(gòu)下,我們?cè)谕瓿梢粋€(gè)訂單流程時(shí)經(jīng)常遇到下面的場(chǎng)景:

  • 一個(gè)訂單創(chuàng)建接口,第一次調(diào)用超時(shí)了,然后調(diào)用方重試了一次
  • 在訂單創(chuàng)建時(shí),我們需要去扣減庫存,這時(shí)接口發(fā)生了超時(shí),調(diào)用方重試了一次
  • 當(dāng)這筆訂單開始支付,在支付請(qǐng)求發(fā)出之后,在服務(wù)端發(fā)生了扣錢操作,接口響應(yīng)超時(shí)了,調(diào)用方重試了一次
  • 一個(gè)訂單狀態(tài)更新接口,調(diào)用方連續(xù)發(fā)送了兩個(gè)消息,一個(gè)是已創(chuàng)建,一個(gè)是已付款。但是你先接收到已付款,然后又接收到了已創(chuàng)建
  • 在支付完成訂單之后,需要發(fā)送一條短信,當(dāng)一臺(tái)機(jī)器接收到短信發(fā)送的消息之后,處理較慢。消息中間件又把消息投遞給另外一臺(tái)機(jī)器處理
  • 以上問題,就是在單體架構(gòu)轉(zhuǎn)成微服務(wù)架構(gòu)之后,帶來的問題。當(dāng)然不是說單體架構(gòu)下沒有這些問題,在單體架構(gòu)下同樣要避免重復(fù)請(qǐng)求。但是出現(xiàn)的問題要比這少得多。

    為了解決以上問題,就需要保證接口的冪等性,接口的冪等性實(shí)際上就是接口可重復(fù)調(diào)用,在調(diào)用方多次調(diào)用的情況下,接口最終得到的結(jié)果是一致的。有些接口可以天然的實(shí)現(xiàn)冪等性,比如查詢接口,對(duì)于查詢來說,你查詢一次和兩次,對(duì)于系統(tǒng)來說,沒有任何影響,查出的結(jié)果也是一樣。

    除了查詢功能具有天然的冪等性之外,增加、更新、刪除都要保證冪等性。那么如何來保證冪等性呢?

    全局唯一ID

    如果使用全局唯一ID,就是根據(jù)業(yè)務(wù)的操作和內(nèi)容生成一個(gè)全局ID,在執(zhí)行操作前先根據(jù)這個(gè)全局唯一ID是否存在,來判斷這個(gè)操作是否已經(jīng)執(zhí)行。如果不存在則把全局ID,存儲(chǔ)到存儲(chǔ)系統(tǒng)中,比如數(shù)據(jù)庫、redis等。如果存在則表示該方法已經(jīng)執(zhí)行。

    從工程的角度來說,使用全局ID做冪等可以作為一個(gè)業(yè)務(wù)的基礎(chǔ)的微服務(wù)存在,在很多的微服務(wù)中都會(huì)用到這樣的服務(wù),在每個(gè)微服務(wù)中都完成這樣的功能,會(huì)存在工作量重復(fù)。另外打造一個(gè)高可靠的冪等服務(wù)還需要考慮很多問題,比如一臺(tái)機(jī)器雖然把全局ID先寫入了存儲(chǔ),但是在寫入之后掛了,這就需要引入全局ID的超時(shí)機(jī)制。

    使用全局唯一ID是一個(gè)通用方案,可以支持插入、更新、刪除業(yè)務(wù)操作。但是這個(gè)方案看起來很美但是實(shí)現(xiàn)起來比較麻煩,下面的方案適用于特定的場(chǎng)景,但是實(shí)現(xiàn)起來比較簡(jiǎn)單。

    去重表

    這種方法適用于在業(yè)務(wù)中有唯一標(biāo)的插入場(chǎng)景中,比如在以上的支付場(chǎng)景中,如果一個(gè)訂單只會(huì)支付一次,所以訂單ID可以作為唯一標(biāo)識(shí)。這時(shí),我們就可以建一張去重表,并且把唯一標(biāo)識(shí)作為唯一索引,在我們實(shí)現(xiàn)時(shí),把創(chuàng)建支付單據(jù)和寫入去去重表,放在一個(gè)事務(wù)中,如果重復(fù)創(chuàng)建,數(shù)據(jù)庫會(huì)拋出唯一約束異常,操作就會(huì)回滾。

    插入或更新

    這種方法插入并且有唯一索引的情況,比如我們要關(guān)聯(lián)商品品類,其中商品的ID和品類的ID可以構(gòu)成唯一索引,并且在數(shù)據(jù)表中也增加了唯一索引。這時(shí)就可以使用InsertOrUpdate操作。在mysql數(shù)據(jù)庫中如下:

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

    多版本控制

    這種方法適合在更新的場(chǎng)景中,比如我們要更新商品的名字,這時(shí)我們就可以在更新的接口中增加一個(gè)版本號(hào),來做冪等

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

    在實(shí)現(xiàn)時(shí)可以如下

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

    狀態(tài)機(jī)控制

    這種方法適合在有狀態(tài)機(jī)流轉(zhuǎn)的情況下,比如就會(huì)訂單的創(chuàng)建和付款,訂單的付款肯定是在之前,這時(shí)我們可以通過在設(shè)計(jì)狀態(tài)字段時(shí),使用int類型,并且通過值類型的大小來做冪等,比如訂單的創(chuàng)建為0,付款成功為100。付款失敗為99

    在做狀態(tài)機(jī)更新時(shí),我們就這可以這樣控制

    update `order` set status=#{status} where id=#{id} and status<#{status}

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

    References

    分布式系統(tǒng)接口冪等性



    作者:南鄉(xiāng)清水
    鏈接:https://www.jianshu.com/p/b5205e58b1ca
    來源:簡(jiǎn)書
    簡(jiǎn)書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。

    轉(zhuǎn)載于:https://www.cnblogs.com/DreamRecorder/p/9243278.html

    總結(jié)

    以上是生活随笔為你收集整理的接口设计的幂等性考虑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。