<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>

          淺談接口冪等性設(shè)計

          共 2692字,需瀏覽 6分鐘

           ·

          2022-04-10 21:12

          本文這里介紹下如何保證服務(wù)接口的冪等性

          abstract.png

          概述

          Idempotent冪等作為一個抽象代數(shù)中的概念。其同樣廣泛應(yīng)用于計算機(jī)科學(xué)領(lǐng)域,意為任意多次執(zhí)行所產(chǎn)生的影響均與一次執(zhí)行的影響相同。實際業(yè)務(wù)中,由于存在表單的重復(fù)提交、MQ消息的重復(fù)消費、服務(wù)間調(diào)用的重試機(jī)制等原因,服務(wù)接口的冪等性就顯得非常重要了。這里從RESTful API的角度對部分常見類型請求的冪等性特點進(jìn)行分析

          • 「GET」:查詢操作。具有天然的冪等性
          • 「POST」:新增操作。顯然請求一次與請求多次造成的結(jié)果是不同的,即不是冪等的
          • 「PUT」:更新操作。如果是以絕對值進(jìn)行更新(例如a=10),則其是冪等的;而如果是通過增量的方式進(jìn)行更新(例如a=a+10),則顯然不是冪等的
          • 「DELETE」:刪除操作。其需要具體分析,例如根據(jù)唯一值進(jìn)行刪除,則是冪等的;而如果是根據(jù)條件進(jìn)行刪除,則就不一定是冪等的。例如每次刪除某字段最大的記錄

          從上可以看出,通常我們所說接口的冪等性也基本是對寫操作(新增、修改、刪除)而言的。下面就來介紹幾種常見的冪等性設(shè)計方案

          唯一性約束

          利用數(shù)據(jù)庫的唯一性約束。當(dāng)?shù)谝淮螖?shù)據(jù)庫新增記錄成功后,后續(xù)再嘗試新增插入相同的數(shù)據(jù)記錄。即會由于違反唯一性約束而操作失敗。在具體實踐過程中,可以直接在業(yè)務(wù)表上對某個具有唯一性特征的業(yè)務(wù)字段添加唯一性約束。此種設(shè)計適用于對業(yè)務(wù)記錄的新增操作,典型地如用戶注冊

          而如果業(yè)務(wù)表沒有業(yè)務(wù)字段可以用來保證唯一性要求,怎么辦呢?其實也很簡單。在業(yè)務(wù)表所在的數(shù)據(jù)庫中單獨建立一張包含全局ID字段的防重表,并對全局ID字段使用唯一性約束。客戶端進(jìn)行業(yè)務(wù)請求時首先從服務(wù)端獲取一個全局的唯一的ID,然后攜帶該全局ID發(fā)起業(yè)務(wù)請求。而在服務(wù)端處理業(yè)務(wù)請求過程中,先向防重表插入全局ID、再執(zhí)行業(yè)務(wù)操作。同時結(jié)合本地事務(wù)以實現(xiàn)如果業(yè)務(wù)操作失敗后,防重表的記錄也可以被回滾,從而保證后續(xù)重試可以成功。可以看到通過引入全局ID、本地防重表的設(shè)計方案,大大拓展了唯一性約束方案的適用領(lǐng)域,使其不僅僅適用于新增操作

          樂觀鎖

          前面提到對于更新操作,如果是以絕對值進(jìn)行更新則是冪等的,例如將count修改為10(count=10);而如果是以增量的方式進(jìn)行更新的,例如將count增加10(count=count+10)。每次的重試操作都會導(dǎo)致count值不斷累加,從而出現(xiàn)冪等性問題。那么對于后者這種情況,我們可以使用樂觀鎖機(jī)制,首先為該表增加一個version版本號字段。每次修改前,客戶端需要先獲取欲修改記錄的version版本號;然后客戶端攜帶該版本號信息發(fā)起更新請求。更新的時候,一方面修改記錄中的相關(guān)業(yè)務(wù)字段,同時也會對該記錄的version版本號字段進(jìn)行自增。這樣即使客戶端發(fā)起重試請求,由于更新條件中的版本號還是之前的,無法在數(shù)據(jù)庫中匹配到相應(yīng)的記錄。自然也就無法成功進(jìn)行修改

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

          該方案同樣適用于更新操作。對于某些場景而言,可以結(jié)合業(yè)務(wù)設(shè)計一個單向的狀態(tài)機(jī)。比如就訂單記錄而言,其狀態(tài)可以有:待支付、支付中、支付成功、支付失敗。這樣我們在訂單表中就可以增加一個status狀態(tài)字段。每次修改操作不僅將status作為條件,同時也會將status字段修改到下一個狀態(tài)值。這樣本次修改成功、后續(xù)即使發(fā)起了重試請求,也會因為由于狀態(tài)條件不匹配無法找到相應(yīng)的記錄而無法進(jìn)行修改。事實上,該方案與樂觀鎖機(jī)制本質(zhì)上是一致的

          Token機(jī)制

          通過Token機(jī)制實現(xiàn)接口的冪等性,更加具有普適性。其基本流程如下:

          1. 客戶端向服務(wù)端請求獲取一個Token,服務(wù)端收到請求后會生成一個全局唯一的ID作為Token并存放到Redis當(dāng)中,同時將Token值返回給客戶端
          2. 客戶端攜帶Step 1獲取到的Token值發(fā)起真正的業(yè)務(wù)請求
          3. 服務(wù)端收到業(yè)務(wù)請求后,校驗請求的Token值是否有效。具體地,判斷客戶端請求攜帶的Token值是否存在于Redis當(dāng)中。如果Redis當(dāng)中存在該Token值則說明校驗成功,進(jìn)入Step 4;如果Redis當(dāng)中不存在該Token值,則說明校驗失敗。換言之,這是一次重復(fù)的操作,本次請求無需進(jìn)行業(yè)務(wù)處理
          4. 刪除Redis當(dāng)中相應(yīng)的Token值
          5. 執(zhí)行業(yè)務(wù)操作

          在該方案中,比較有爭議的點在于是先刪除Redis中的Token還是先執(zhí)行業(yè)務(wù)操作?

          • 先刪除Redis中的Token,即上文的流程順序。則一方面需要保證 判斷Token是否存在于Redis、刪除Redis中的Token 這兩個操作的原子性,可以通過Lua腳本來實現(xiàn)。以避免高并發(fā)環(huán)境下,多個請求同時通過了Token校驗。而如果不想用Lua腳本的話,也可以選擇直接刪除Token、然后利用返回值判斷是否刪除成功的方式來進(jìn)行Token校驗;另一方面,如果Token被成功刪除了,但業(yè)務(wù)操作卻由于某種原因未得到成功執(zhí)行,會導(dǎo)致后續(xù)的重試請求也無法完成業(yè)務(wù)操作。因為此時Token已經(jīng)被刪除掉了。解決辦法也很簡單,客戶端重新獲取一個新Token再次發(fā)起業(yè)務(wù)請求即可
          • 先執(zhí)行業(yè)務(wù)操作,則需要保證這之后的 刪除Redis中的Token操作 一定要執(zhí)行成功,否則即會導(dǎo)致出現(xiàn)冪等性問題。與此同時還需要對整個流程(Token校驗、執(zhí)行業(yè)務(wù)、刪除Token)加鎖,以避免重試的請求同時通過了Token校驗 綜上所述,推薦的處理方式是先刪除Token再執(zhí)行業(yè)務(wù)操作

          基于Redis的分布式鎖

          事實上,前面提到的基于全局ID、防重表的唯一性約束方案設(shè)計,本質(zhì)上相當(dāng)于是基于數(shù)據(jù)庫的分布式鎖。類似地,我們同樣可以利用基于Redis的分布式鎖實現(xiàn)冪等性。具體地

          1. 客戶端向服務(wù)端請求獲取一個全局ID,服務(wù)端會返回一個全局唯一的ID給客戶端
          2. 客戶端攜帶Step 1獲取到的全局ID發(fā)起真正的業(yè)務(wù)請求
          3. 服務(wù)端收到業(yè)務(wù)請求后,向Redis中存儲該請求攜帶的全局ID、配置一定的TTL過期時間。這里要求 「當(dāng)且僅當(dāng)Redis中不存在該Key時才會進(jìn)行存儲、并且需要Redis操作成功」 才會視為設(shè)置成功;如果Redis中已經(jīng)存在該Key則不再進(jìn)行設(shè)置并直接視為設(shè)置失敗
          4. 根據(jù)Step 3的執(zhí)行結(jié)果,如果設(shè)置成功,則說明是第一次請求,允許執(zhí)行業(yè)務(wù)操作;反之,則視為重復(fù)請求,不允進(jìn)行業(yè)務(wù)操作

          這里關(guān)于Step 3的實現(xiàn)方式,一方面可以通過setnx、expire命令實現(xiàn),并結(jié)合Lua腳本保證原子性;另一方面,如果不想使用Lua腳本,則可以使用set命令。Redis從2.6.12版本開始對set命令進(jìn)行了增強(qiáng),提供了對NX、EX選項的支持

          瀏覽 38
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  欧美性猛交ⅩXXX乱大交吃奶 | 无码视频免费在线播放 | 久久这里只有精品99 | 日日碰狠狠| 色香视频在线观看 |