<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot 接口冪等性實現(xiàn)的4種方案!這個我真的服氣了!

          共 10385字,需瀏覽 21分鐘

           ·

          2021-02-01 23:30

          作者:超級小豆丁

          http://www.mydlq.club/article/94/

          目錄

          • 什么是冪等性
          • 什么是接口冪等性
          • 為什么需要實現(xiàn)冪等性
          • 引入冪等性后對系統(tǒng)的影響
          • Restful API 接口的冪等性
          • 如何實現(xiàn)冪等性
            • 方案一:數(shù)據(jù)庫唯一主鍵
            • 方案二:數(shù)據(jù)庫樂觀鎖
            • 方案三:防重 Token 令牌
            • 方案四、下游傳遞唯一序列號
          • 實現(xiàn)接口冪等示例
            • Maven 引入相關(guān)依賴
            • 配置連接 Redis 的參數(shù)
            • 創(chuàng)建與驗證 Token 工具類
            • 創(chuàng)建測試的 Controller 類
            • 創(chuàng)建 SpringBoot 啟動類
            • 寫測試類進行測試
          • 最后總結(jié)

          系統(tǒng)環(huán)境:

          • Java JDK 版本:1.8

          • SpringBoot 版本:2.3.4.RELEASE

          示例地址:

          https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-idempotent-token/

          一、什么是冪等性

          冪等是一個數(shù)學與計算機學概念,在數(shù)學中某一元運算為冪等時,其作用在任一元素兩次后會和其作用一次的結(jié)果相同。在計算機中編程中,一個冪等操作的特點是其任意多次執(zhí)行所產(chǎn)生的影響均與一次執(zhí)行的影響相同。

          冪等函數(shù)或冪等方法是指可以使用相同參數(shù)重復執(zhí)行,并能獲得相同結(jié)果的函數(shù)。這些函數(shù)不會影響系統(tǒng)狀態(tài),也不用擔心重復執(zhí)行會對系統(tǒng)造成改變。

          二、什么是接口冪等性

          在HTTP/1.1中,對冪等性進行了定義。它描述了一次和多次請求某一個資源對于資源本身應(yīng)該具有同樣的結(jié)果(網(wǎng)絡(luò)超時等問題除外),即第一次請求的時候?qū)Y源產(chǎn)生了副作用,但是以后的多次請求都不會再對資源產(chǎn)生副作用。

          這里的副作用是不會對結(jié)果產(chǎn)生破壞或者產(chǎn)生不可預料的結(jié)果。也就是說,其任意多次執(zhí)行對資源本身所產(chǎn)生的影響均與一次執(zhí)行的影響相同。

          三、為什么需要實現(xiàn)冪等性

          在接口調(diào)用時一般情況下都能正常返回信息不會重復提交,不過在遇見以下情況時可以就會出現(xiàn)問題,如:

          • 前端重復提交表單:?在填寫一些表格時候,用戶填寫完成提交,很多時候會因網(wǎng)絡(luò)波動沒有及時對用戶做出提交成功響應(yīng),致使用戶認為沒有成功提交,然后一直點提交按鈕,這時就會發(fā)生重復提交表單請求。

          • 用戶惡意進行刷單:?例如在實現(xiàn)用戶投票這種功能時,如果用戶針對一個用戶進行重復提交投票,這樣會導致接口接收到用戶重復提交的投票信息,這樣會使投票結(jié)果與事實嚴重不符。

          • 接口超時重復提交:?很多時候 HTTP 客戶端工具都默認開啟超時重試的機制,尤其是第三方調(diào)用接口時候,為了防止網(wǎng)絡(luò)波動超時等造成的請求失敗,都會添加重試機制,導致一個請求提交多次。

          • 消息進行重復消費:?當使用 MQ 消息中間件時候,如果發(fā)生消息中間件出現(xiàn)錯誤未及時提交消費信息,導致發(fā)生重復消費。

          使用冪等性最大的優(yōu)勢在于使接口保證任何冪等性操作,免去因重試等造成系統(tǒng)產(chǎn)生的未知的問題。

          四、引入冪等性后對系統(tǒng)的影響

          冪等性是為了簡化客戶端邏輯處理,能放置重復提交等操作,但卻增加了服務(wù)端的邏輯復雜性和成本,其主要是:

          • 把并行執(zhí)行的功能改為串行執(zhí)行,降低了執(zhí)行效率。

          • 增加了額外控制冪等的業(yè)務(wù)邏輯,復雜化了業(yè)務(wù)功能;

          所以在使用時候需要考慮是否引入冪等性的必要性,根據(jù)實際業(yè)務(wù)場景具體分析,除了業(yè)務(wù)上的特殊要求外,一般情況下不需要引入的接口冪等性。

          五、Restful API 接口的冪等性

          現(xiàn)在流行的?Restful 推薦的幾種?HTTP 接口方法中,分別存在冪等行與不能保證冪等的方法,如下:

          • √ 滿足冪等

          • x 不滿足冪等

          • - 可能滿足也可能不滿足冪等,根據(jù)實際業(yè)務(wù)邏輯有關(guān)

          六、如何實現(xiàn)冪等性

          方案一:數(shù)據(jù)庫唯一主鍵

          方案描述

          數(shù)據(jù)庫唯一主鍵的實現(xiàn)主要是利用數(shù)據(jù)庫中主鍵唯一約束的特性,一般來說唯一主鍵比較適用于“插入”時的冪等性,其能保證一張表中只能存在一條帶該唯一主鍵的記錄。

          使用數(shù)據(jù)庫唯一主鍵完成冪等性時需要注意的是,該主鍵一般來說并不是使用數(shù)據(jù)庫中自增主鍵,而是使用分布式 ID 充當主鍵,這樣才能能保證在分布式環(huán)境下 ID 的全局唯一性。

          適用操作:

          • 插入操作

          • 刪除操作

          使用限制:

          • 需要生成全局唯一主鍵 ID;

          主要流程:

          主要流程:

          • ① 客戶端執(zhí)行創(chuàng)建請求,調(diào)用服務(wù)端接口。

          • ② 服務(wù)端執(zhí)行業(yè)務(wù)邏輯,生成一個分布式 ID,將該 ID 充當待插入數(shù)據(jù)的主鍵,然后執(zhí)數(shù)據(jù)插入操作,運行對應(yīng)的 SQL 語句。

          • ③ 服務(wù)端將該條數(shù)據(jù)插入數(shù)據(jù)庫中,如果插入成功則表示沒有重復調(diào)用接口。如果拋出主鍵重復異常,則表示數(shù)據(jù)庫中已經(jīng)存在該條記錄,返回錯誤信息到客戶端。

          方案二:數(shù)據(jù)庫樂觀鎖

          方案描述:

          數(shù)據(jù)庫樂觀鎖方案一般只能適用于執(zhí)行“更新操作”的過程,我們可以提前在對應(yīng)的數(shù)據(jù)表中多添加一個字段,充當當前數(shù)據(jù)的版本標識。這樣每次對該數(shù)據(jù)庫該表的這條數(shù)據(jù)執(zhí)行更新時,都會將該版本標識作為一個條件,值為上次待更新數(shù)據(jù)中的版本標識的值。

          適用操作:

          • 更新操作

          使用限制:

          • 需要數(shù)據(jù)庫對應(yīng)業(yè)務(wù)表中添加額外字段;

          描述示例:

          例如,存在如下的數(shù)據(jù)表中:

          為了每次執(zhí)行更新時防止重復更新,確定更新的一定是要更新的內(nèi)容,我們通常都會添加一個 version 字段記錄當前的記錄版本,這樣在更新時候?qū)⒃撝祹希敲粗灰獔?zhí)行更新操作就能確定一定更新的是某個對應(yīng)版本下的信息。這樣每次執(zhí)行更新時候,都要指定要更新的版本號,如下操作就能準確更新 version=5 的信息:

          UPDATE?my_table?SET?price=price+50,version=version+1?WHERE?id=1?AND?version=5

          上面 WHERE 后面跟著條件 id=1 AND version=5 被執(zhí)行后,id=1 的 version 被更新為 6,所以如果重復執(zhí)行該條 SQL 語句將不生效,因為 id=1 AND version=5 的數(shù)據(jù)已經(jīng)不存在,這樣就能保住更新的冪等,多次更新對結(jié)果不會產(chǎn)生影響。

          方案三:防重 Token 令牌

          方案描述:

          針對客戶端連續(xù)點擊或者調(diào)用方的超時重試等情況,例如提交訂單,此種操作就可以用 Token 的機制實現(xiàn)防止重復提交。

          簡單的說就是調(diào)用方在調(diào)用接口的時候先向后端請求一個全局 ID(Token),請求的時候攜帶這個全局 ID 一起請求(Token 最好將其放到 Headers 中),后端需要對這個 Token 作為 Key,用戶信息作為 Value 到 Redis 中進行鍵值內(nèi)容校驗,如果 Key 存在且 Value 匹配就執(zhí)行刪除命令,然后正常執(zhí)行后面的業(yè)務(wù)邏輯。如果不存在對應(yīng)的 Key 或 Value 不匹配就返回重復執(zhí)行的錯誤信息,這樣來保證冪等操作。

          適用操作:

          • 插入操作

          • 更新操作

          • 刪除操作

          使用限制:

          • 需要生成全局唯一 Token 串;

          • 需要使用第三方組件 Redis 進行數(shù)據(jù)效驗;

          主要流程:

          • ① 服務(wù)端提供獲取 Token 的接口,該 Token 可以是一個序列號,也可以是一個分布式 ID 或者 UUID 串。

          • ② 客戶端調(diào)用接口獲取 Token,這時候服務(wù)端會生成一個 Token 串。

          • ③ 然后將該串存入 Redis 數(shù)據(jù)庫中,以該 Token 作為 Redis 的鍵(注意設(shè)置過期時間)。

          • ④ 將 Token 返回到客戶端,客戶端拿到后應(yīng)存到表單隱藏域中。

          • ⑤ 客戶端在執(zhí)行提交表單時,把 Token 存入到 Headers 中,執(zhí)行業(yè)務(wù)請求帶上該 Headers。

          • ⑥ 服務(wù)端接收到請求后從 Headers 中拿到 Token,然后根據(jù) Token 到 Redis 中查找該 key 是否存在。

          • ⑦ 服務(wù)端根據(jù) Redis 中是否存該 key 進行判斷,如果存在就將該 key 刪除,然后正常執(zhí)行業(yè)務(wù)邏輯。如果不存在就拋異常,返回重復提交的錯誤信息。

          注意,在并發(fā)情況下,執(zhí)行 Redis 查找數(shù)據(jù)與刪除需要保證原子性,否則很可能在并發(fā)下無法保證冪等性。其實現(xiàn)方法可以使用分布式鎖或者使用 Lua 表達式來注銷查詢與刪除操作。

          方案四、下游傳遞唯一序列號

          方案描述:

          所謂請求序列號,其實就是每次向服務(wù)端請求時候附帶一個短時間內(nèi)唯一不重復的序列號,該序列號可以是一個有序 ID,也可以是一個訂單號,一般由下游生成,在調(diào)用上游服務(wù)端接口時附加該序列號和用于認證的 ID。

          當上游服務(wù)器收到請求信息后拿取該 序列號 和下游 認證ID 進行組合,形成用于操作 Redis 的 Key,然后到 Redis 中查詢是否存在對應(yīng)的 Key 的鍵值對,根據(jù)其結(jié)果:

          • 如果存在,就說明已經(jīng)對該下游的該序列號的請求進行了業(yè)務(wù)處理,這時可以直接響應(yīng)重復請求的錯誤信息。

          • 如果不存在,就以該 Key 作為 Redis 的鍵,以下游關(guān)鍵信息作為存儲的值(例如下游商傳遞的一些業(yè)務(wù)邏輯信息),將該鍵值對存儲到 Redis 中 ,然后再正常執(zhí)行對應(yīng)的業(yè)務(wù)邏輯即可。

          適用操作:

          • 插入操作

          • 更新操作

          • 刪除操作

          使用限制:

          • 要求第三方傳遞唯一序列號;

          • 需要使用第三方組件 Redis 進行數(shù)據(jù)效驗;

          主要流程:

          主要步驟:

          • ① 下游服務(wù)生成分布式 ID?作為序列號,然后執(zhí)行請求調(diào)用上游接口,并附帶“唯一序列號”與請求的“認證憑據(jù)ID”。

          • ② 上游服務(wù)進行安全效驗,檢測下游傳遞的參數(shù)中是否存在“序列號”和“憑據(jù)ID”。

          • ③ 上游服務(wù)到 Redis 中檢測是否存在對應(yīng)的“序列號”與“認證ID”組成的 Key,如果存在就拋出重復執(zhí)行的異常信息,然后響應(yīng)下游對應(yīng)的錯誤信息。如果不存在就以該“序列號”和“認證ID”組合作為 Key,以下游關(guān)鍵信息作為 Value,進而存儲到 Redis 中,然后正常執(zhí)行接來來的業(yè)務(wù)邏輯。

          上面步驟中插入數(shù)據(jù)到 Redis 一定要設(shè)置過期時間。這樣能保證在這個時間范圍內(nèi),如果重復調(diào)用接口,則能夠進行判斷識別。如果不設(shè)置過期時間,很可能導致數(shù)據(jù)無限量的存入 Redis,致使 Redis 不能正常工作。

          七、實現(xiàn)接口冪等示例

          這里使用防重 Token 令牌方案,該方案能保證在不同請求動作下的冪等性,實現(xiàn)邏輯可以看上面寫的”防重 Token 令牌”方案,接下來寫下實現(xiàn)這個邏輯的代碼。

          1、Maven 引入相關(guān)依賴

          這里使用 Maven 工具管理依賴,這里在 pom.xml 中引入 SpringBoot、Redis、lombok 相關(guān)依賴。


          <project?xmlns="http://maven.apache.org/POM/4.0.0"?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          ?????????xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?https://maven.apache.org/xsd/maven-4.0.0.xsd">

          ????<modelVersion>4.0.0modelVersion>

          ????<parent>
          ????????<groupId>org.springframework.bootgroupId>
          ????????<artifactId>spring-boot-starter-parentartifactId>
          ????????<version>2.3.4.RELEASEversion>
          ????parent>

          ????<groupId>mydlq.clubgroupId>
          ????<artifactId>springboot-idempotent-tokenartifactId>
          ????<version>0.0.1version>
          ????<name>springboot-idempotent-tokenname>
          ????<description>Idempotent?Demodescription>

          ????<properties>
          ????????<java.version>1.8java.version>
          ????properties>

          ????<dependencies>
          ????????
          ????????<dependency>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-starter-webartifactId>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-starter-data-redisartifactId>
          ????????dependency>
          ????????<dependency>
          ????????????<groupId>org.apache.commonsgroupId>
          ????????????<artifactId>commons-pool2artifactId>
          ????????dependency>
          ????????
          ????????<dependency>
          ????????????<groupId>org.projectlombokgroupId>
          ????????????<artifactId>lombokartifactId>
          ????????dependency>
          ????dependencies>

          ????<build>
          ????????<plugins>
          ????????????<plugin>
          ????????????????<groupId>org.springframework.bootgroupId>
          ????????????????<artifactId>spring-boot-maven-pluginartifactId>
          ????????????plugin>
          ????????plugins>
          ????build>

          project>

          2、配置連接 Redis 的參數(shù)

          在 application 配置文件中配置連接 Redis 的參數(shù),如下:

          spring:
          ??redis:
          ????ssl:?false
          ????host:?127.0.0.1
          ????port:?6379
          ????database:?0
          ????timeout:?1000
          ????password:
          ????lettuce:
          ??????pool:
          ????????max-active:?100
          ????????max-wait:?-1
          ????????min-idle:?0
          ????????max-idle:?20

          3、創(chuàng)建與驗證 Token 工具類

          創(chuàng)建用于操作 Token 相關(guān)的 Service 類,里面存在 Token 創(chuàng)建與驗證方法,其中:

          • Token 創(chuàng)建方法:?使用 UUID 工具創(chuàng)建 Token 串,設(shè)置以 “idempotent_token:“+“Token串” 作為 Key,以用戶信息當成 Value,將信息存入 Redis 中。

          • Token 驗證方法:?接收 Token 串參數(shù),加上 Key 前綴形成 Key,再傳入 value 值,執(zhí)行 Lua 表達式(Lua 表達式能保證命令執(zhí)行的原子性)進行查找對應(yīng) Key 與刪除操作。執(zhí)行完成后驗證命令的返回結(jié)果,如果結(jié)果不為空且非0,則驗證成功,否則失敗。

          import?java.util.Arrays;
          import?java.util.UUID;
          import?java.util.concurrent.TimeUnit;
          import?lombok.extern.slf4j.Slf4j;
          import?org.springframework.beans.factory.annotation.Autowired;
          import?org.springframework.data.redis.core.StringRedisTemplate;
          import?org.springframework.data.redis.core.script.DefaultRedisScript;
          import?org.springframework.data.redis.core.script.RedisScript;
          import?org.springframework.stereotype.Service;

          @Slf4j
          @Service
          public?class?TokenUtilService?{

          ????@Autowired
          ????private?StringRedisTemplate?redisTemplate;

          ????/**
          ?????*?存入?Redis?的?Token?鍵的前綴
          ?????*/

          ????private?static?final?String?IDEMPOTENT_TOKEN_PREFIX?=?"idempotent_token:";

          ????/**
          ?????*?創(chuàng)建?Token?存入?Redis,并返回該?Token
          ?????*
          ?????*?@param?value?用于輔助驗證的?value?值
          ?????*?@return?生成的?Token?串
          ?????*/

          ????public?String?generateToken(String?value)?{
          ????????//?實例化生成?ID?工具對象
          ????????String?token?=?UUID.randomUUID().toString();
          ????????//?設(shè)置存入?Redis?的?Key
          ????????String?key?=?IDEMPOTENT_TOKEN_PREFIX?+?token;
          ????????//?存儲?Token?到?Redis,且設(shè)置過期時間為5分鐘
          ????????redisTemplate.opsForValue().set(key,?value,?5,?TimeUnit.MINUTES);
          ????????//?返回?Token
          ????????return?token;
          ????}

          ????/**
          ?????*?驗證?Token?正確性
          ?????*
          ?????*?@param?token?token?字符串
          ?????*?@param?value?value?存儲在Redis中的輔助驗證信息
          ?????*?@return?驗證結(jié)果
          ?????*/

          ????public?boolean?validToken(String?token,?String?value)?{
          ????????//?設(shè)置?Lua?腳本,其中?KEYS[1]?是?key,KEYS[2]?是?value
          ????????String?script?=?"if?redis.call('get',?KEYS[1])?==?KEYS[2]?then?return?redis.call('del',?KEYS[1])?else?return?0?end";
          ????????RedisScript?redisScript?=?new?DefaultRedisScript<>(script,?Long.class);
          ????????//?根據(jù)?Key?前綴拼接?Key
          ????????String?key?=?IDEMPOTENT_TOKEN_PREFIX?+?token;
          ????????//?執(zhí)行?Lua?腳本
          ????????Long?result?=?redisTemplate.execute(redisScript,?Arrays.asList(key,?value));
          ????????//?根據(jù)返回結(jié)果判斷是否成功成功匹配并刪除?Redis?鍵值對,若果結(jié)果不為空和0,則驗證通過
          ????????if?(result?!=?null?&&?result?!=?0L)?{
          ????????????log.info("驗證?token={},key={},value={}?成功",?token,?key,?value);
          ????????????return?true;
          ????????}
          ????????log.info("驗證?token={},key={},value={}?失敗",?token,?key,?value);
          ????????return?false;
          ????}

          }

          4、創(chuàng)建測試的 Controller 類

          創(chuàng)建用于測試的 Controller 類,里面有獲取 Token 與測試接口冪等性的接口,內(nèi)容如下:

          import?lombok.extern.slf4j.Slf4j;
          import?mydlq.club.example.service.TokenUtilService;
          import?org.springframework.beans.factory.annotation.Autowired;
          import?org.springframework.web.bind.annotation.*;

          @Slf4j
          @RestController
          public?class?TokenController?{

          ????@Autowired
          ????private?TokenUtilService?tokenService;

          ????/**
          ?????*?獲取?Token?接口
          ?????*
          ?????*?@return?Token?串
          ?????*/

          ????@GetMapping("/token")
          ????public?String?getToken()?{
          ????????//?獲取用戶信息(這里使用模擬數(shù)據(jù))
          ????????//?注:這里存儲該內(nèi)容只是舉例,其作用為輔助驗證,使其驗證邏輯更安全,如這里存儲用戶信息,其目的為:
          ????????//?-?1)、使用"token"驗證?Redis?中是否存在對應(yīng)的?Key
          ????????//?- 2)、使用"用戶信息"驗證 Redis 的 Value 是否匹配。
          ????????String?userInfo?=?"mydlq";
          ????????//?獲取?Token?字符串,并返回
          ????????return?tokenService.generateToken(userInfo);
          ????}

          ????/**
          ?????*?接口冪等性測試接口
          ?????*
          ?????*?@param?token?冪等?Token?串
          ?????*?@return?執(zhí)行結(jié)果
          ?????*/

          ????@PostMapping("/test")
          ????public?String?test(@RequestHeader(value?=?"token")?String?token)?{
          ????????//?獲取用戶信息(這里使用模擬數(shù)據(jù))
          ????????String?userInfo?=?"mydlq";
          ????????//?根據(jù)?Token?和與用戶相關(guān)的信息到?Redis?驗證是否存在對應(yīng)的信息
          ????????boolean?result?=?tokenService.validToken(token,?userInfo);
          ????????//?根據(jù)驗證結(jié)果響應(yīng)不同信息
          ????????return?result???"正常調(diào)用"?:?"重復調(diào)用";
          ????}

          }

          5、創(chuàng)建 SpringBoot 啟動類

          創(chuàng)建啟動類,用于啟動 SpringBoot 應(yīng)用。

          import?org.springframework.boot.SpringApplication;
          import?org.springframework.boot.autoconfigure.SpringBootApplication;

          @SpringBootApplication
          public?class?Application?{

          ????public?static?void?main(String[]?args)?{
          ????????SpringApplication.run(Application.class,?args);
          ????}

          }

          6、寫測試類進行測試

          寫個測試類進行測試,多次訪問同一個接口,測試是否只有第一次能否執(zhí)行成功。

          import?org.junit.Assert;
          import?org.junit.Test;
          import?org.junit.runner.RunWith;
          import?lombok.extern.slf4j.Slf4j;
          import?org.springframework.beans.factory.annotation.Autowired;
          import?org.springframework.boot.test.context.SpringBootTest;
          import?org.springframework.http.MediaType;
          import?org.springframework.test.context.junit4.SpringRunner;
          import?org.springframework.test.web.servlet.MockMvc;
          import?org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
          import?org.springframework.test.web.servlet.setup.MockMvcBuilders;
          import?org.springframework.web.context.WebApplicationContext;

          @Slf4j
          @SpringBootTest
          @RunWith(SpringRunner.class)
          public?class?IdempotenceTest?
          {

          ????@Autowired
          ????private?WebApplicationContext?webApplicationContext;

          ????@Test
          ????public?void?interfaceIdempotenceTest()?throws?Exception?{
          ????????//?初始化?MockMvc
          ????????MockMvc?mockMvc?=?MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
          ????????//?調(diào)用獲取?Token?接口
          ????????String?token?=?mockMvc.perform(MockMvcRequestBuilders.get("/token")
          ????????????????.accept(MediaType.TEXT_HTML))
          ????????????????.andReturn()
          ????????????????.getResponse().getContentAsString();
          ????????log.info("獲取的 Token 串:{}",?token);
          ????????//?循環(huán)調(diào)用?5?次進行測試
          ????????for?(int?i?=?1;?i?<=?5;?i++)?{
          ????????????log.info("第{}次調(diào)用測試接口",?i);
          ????????????//?調(diào)用驗證接口并打印結(jié)果
          ????????????String?result?=?mockMvc.perform(MockMvcRequestBuilders.post("/test")
          ????????????????????.header("token",?token)
          ????????????????????.accept(MediaType.TEXT_HTML))
          ????????????????????.andReturn().getResponse().getContentAsString();
          ????????????log.info(result);
          ????????????//?結(jié)果斷言
          ????????????if?(i?==?0)?{
          ????????????????Assert.assertEquals(result,?"正常調(diào)用");
          ????????????}?else?{
          ????????????????Assert.assertEquals(result,?"重復調(diào)用");
          ????????????}
          ????????}
          ????}

          }

          顯示如下:

          [main] IdempotenceTest:??獲取的 Token 串:980ea707-ce2e-456e-a059-0a03332110b4
          [main]?IdempotenceTest:??第1次調(diào)用測試接口
          [main]?IdempotenceTest:??正常調(diào)用
          [main]?IdempotenceTest:??第2次調(diào)用測試接口
          [main]?IdempotenceTest:??重復調(diào)用
          [main]?IdempotenceTest:??第3次調(diào)用測試接口
          [main]?IdempotenceTest:??重復調(diào)用
          [main]?IdempotenceTest:??第4次調(diào)用測試接口
          [main]?IdempotenceTest:??重復調(diào)用
          [main]?IdempotenceTest:??第5次調(diào)用測試接口
          [main]?IdempotenceTest:??重復調(diào)用

          八、最后總結(jié)

          冪等性是開發(fā)當中很常見也很重要的一個需求,尤其是支付、訂單等與金錢掛鉤的服務(wù),保證接口冪等性尤其重要。在實際開發(fā)中,我們需要針對不同的業(yè)務(wù)場景我們需要靈活的選擇冪等性的實現(xiàn)方式:

          • 對于下單等存在唯一主鍵的,可以使用“唯一主鍵方案”的方式實現(xiàn)。

          • 對于更新訂單狀態(tài)等相關(guān)的更新場景操作,使用“樂觀鎖方案”實現(xiàn)更為簡單。

          • 對于上下游這種,下游請求上游,上游服務(wù)可以使用“下游傳遞唯一序列號方案”更為合理。

          • 類似于前端重復提交、重復下單、沒有唯一ID號的場景,可以通過 Token 與 Redis 配合的“防重 Token 方案”實現(xiàn)更為快捷。

          上面只是給與一些建議,再次強調(diào)一下,實現(xiàn)冪等性需要先理解自身業(yè)務(wù)需求,根據(jù)業(yè)務(wù)邏輯來實現(xiàn)這樣才合理,處理好其中的每一個結(jié)點細節(jié),完善整體的業(yè)務(wù)流程設(shè)計,才能更好的保證系統(tǒng)的正常運行。最后做一個簡單總結(jié),然后本博文到此結(jié)束,如下:


          ↓↓↓↓點擊下方獲取源碼和教程↓↓↓↓

          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美日韩小穴 | 天堂中文在线观看 | 成人做爰黄AA片免费看三区 | 久久久久久久久久艹 | 久久久久久久视频 |