jinfeng_wang

          G-G-S,D-D-U!

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks
          http://www.tuicool.com/articles/fymANrJ


          冪等概念來自數(shù)學(xué),表示N次變換和1次變換的結(jié)果是相同的。這里討論在某些場景下,客戶端在調(diào)用服務(wù)沒有達(dá)到預(yù)期結(jié)果時(shí),會(huì)進(jìn)行多次調(diào)用,為避免多次重復(fù)的調(diào)用對服務(wù)資源產(chǎn)生副作用,服務(wù)提供者會(huì)承諾滿足冪等。

          舉個(gè)栗子,雙十一零點(diǎn)剛過,小明就迫不及待地點(diǎn)擊提交訂單按鈕,選擇在線支付,點(diǎn)了確認(rèn)支付按鈕,這時(shí)候網(wǎng)絡(luò)有些慢,小明擔(dān)心心愛的商品被搶購一空,就點(diǎn)了多次確認(rèn)付款按鈕,如果這個(gè)訂單扣款多次,客服熱線估計(jì)會(huì)被小明打爆。

          什么是冪等性

          HTTP/1.1中對冪等性的定義是:一次和多次請求某一個(gè)資源 對于資源本身 應(yīng)該具有同樣的副作用(網(wǎng)絡(luò)超時(shí)等問題除外)。也就是說, 其任意多次執(zhí)行對資源本身所產(chǎn)生的影響均與一次執(zhí)行的影響相同 。

          Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

          這里需要關(guān)注幾個(gè)重點(diǎn):

          1. 冪等不僅僅只是一次(或多次)請求對資源沒有副作用(比如查詢數(shù)據(jù)庫操作,沒有增刪改,因此沒有對數(shù)據(jù)庫有任何影響)。
          2. 冪等還包括第一次請求的時(shí)候?qū)Y源產(chǎn)生了副作用,但是以后的多次請求都不會(huì)再對資源產(chǎn)生副作用。
          3. 冪等關(guān)注的是以后的多次請求是否對資源產(chǎn)生的副作用,而不關(guān)注結(jié)果。
          4. 網(wǎng)絡(luò)超時(shí)等問題,不是冪等的討論范圍。

          冪等性是系統(tǒng)服務(wù)對外一種承諾(而不是實(shí)現(xiàn)),承諾只要調(diào)用接口成功,外部多次調(diào)用對系統(tǒng)的影響是一致的。聲明為冪等的服務(wù)會(huì)認(rèn)為外部調(diào)用失敗是常態(tài),并且失敗之后必然會(huì)有重試。

          什么情況下需要冪等

          業(yè)務(wù)開發(fā)中,經(jīng)常會(huì)遇到重復(fù)提交的情況,無論是由于網(wǎng)絡(luò)問題無法收到請求結(jié)果而重新發(fā)起請求,或是前端的操作抖動(dòng)而造成重復(fù)提交情況。

          在交易系統(tǒng),支付系統(tǒng)這種重復(fù)提交造成的問題有尤其明顯,比如:

          1. 用戶在APP上連續(xù)點(diǎn)擊了多次提交訂單,后臺應(yīng)該只產(chǎn)生一個(gè)訂單;
          2. 向支付寶發(fā)起支付請求,由于網(wǎng)絡(luò)問題或系統(tǒng)BUG重發(fā),支付寶應(yīng)該只扣一次錢。
            很顯然,聲明冪等的服務(wù)認(rèn)為,外部調(diào)用者會(huì)存在多次調(diào)用的情況,為了防止外部多次調(diào)用對系統(tǒng)數(shù)據(jù)狀態(tài)的發(fā)生多次改變,將服務(wù)設(shè)計(jì)成冪等。

          冪等VS防重

          上面例子中小明遇到的問題,只是重復(fù)提交的情況,和服務(wù)冪等的初衷是不同的。重復(fù)提交是在第一次請求已經(jīng)成功的情況下,人為的進(jìn)行多次操作,導(dǎo)致不滿足冪等要求的服務(wù)多次改變狀態(tài)。而冪等更多使用的情況是第一次請求不知道結(jié)果(比如超時(shí))或者失敗的異常情況下,發(fā)起多次請求,目的是多次確認(rèn)第一次請求成功,卻不會(huì)因多次請求而出現(xiàn)多次的狀態(tài)變化。

          什么情況下需要保證冪等性

          以SQL為例,有下面三種場景,只有第三種場景需要開發(fā)人員使用其他策略保證冪等性:

          1. SELECT col1 FROM tab1 WHER col2=2 ,無論執(zhí)行多少次都不會(huì)改變狀態(tài),是天然的冪等。
          2. UPDATE tab1 SET col1=1 WHERE col2=2 ,無論執(zhí)行 成功 多少次 狀態(tài) 都是一致的,因此也是冪等操作。
          3. UPDATE tab1 SET col1=col1+1 WHERE col2=2 ,每次執(zhí)行的結(jié)果都會(huì)發(fā)生變化,這種不是冪等的。

          為什么要設(shè)計(jì)冪等性的服務(wù)

          冪等可以使得客戶端邏輯處理變得簡單,但是卻以服務(wù)邏輯變得復(fù)雜為代價(jià)。滿足冪等服務(wù)的需要在邏輯中至少包含兩點(diǎn):

          1. 首先去查詢上一次的執(zhí)行狀態(tài),如果沒有則認(rèn)為是第一次請求
          2. 在服務(wù)改變狀態(tài)的業(yè)務(wù)邏輯前,保證防重復(fù)提交的邏輯

          冪等的不足

          冪等是為了簡化客戶端邏輯處理,卻增加了服務(wù)提供者的邏輯和成本,是否有必要,需要根據(jù)具體場景具體分析,因此除了業(yè)務(wù)上的特殊要求外,盡量不提供冪等的接口。

          1. 增加了額外控制冪等的業(yè)務(wù)邏輯,復(fù)雜化了業(yè)務(wù)功能;
          2. 把并行執(zhí)行的功能改為串行執(zhí)行,降低了執(zhí)行效率。

          保證冪等策略

          冪等需要通過 唯一的業(yè)務(wù)單號 來保證。也就是說相同的業(yè)務(wù)單號,認(rèn)為是同一筆業(yè)務(wù)。使用這個(gè)唯一的業(yè)務(wù)單號來確保,后面多次的相同的業(yè)務(wù)單號的處理邏輯和執(zhí)行效果是一致的。

          下面以支付為例,在不考慮并發(fā)的情況下,實(shí)現(xiàn)冪等很簡單:①先查詢一下訂單是否已經(jīng)支付過,②如果已經(jīng)支付過,則返回支付成功;如果沒有支付,進(jìn)行支付流程,修改訂單狀態(tài)為‘已支付’。

          防重復(fù)提交策略

          上述的保證冪等方案是分成兩步的,第②步依賴第①步的查詢結(jié)果,無法保證原子性的。在高并發(fā)下就會(huì)出現(xiàn)下面的情況:第二次請求在第一次請求第②步訂單狀態(tài)還沒有修改為‘已支付狀態(tài)’的情況下到來。既然得出了這個(gè)結(jié)論,余下的問題也就變得簡單:把查詢和變更狀態(tài)操作加鎖,將并行操作改為串行操作。

          樂觀鎖

          如果只是更新 已有 的數(shù)據(jù),沒有必要對業(yè)務(wù)進(jìn)行加鎖,設(shè)計(jì)表結(jié)構(gòu)時(shí)使用樂觀鎖,一般通過version來做樂觀鎖,這樣既能保證執(zhí)行效率,又能保證冪等。例如:

          UPDATE tab1 SET col1=1,version=version+1 WHERE version=#version# 

          不過,樂觀鎖存在失效的情況,就是常說的ABA問題,不過如果version版本一直是自增的就不會(huì)出現(xiàn)ABA的情況。(從網(wǎng)上找了一張圖片很能說明樂觀鎖,引用過來,出自 Mybatis對樂觀鎖的支持 )

          防重表

          使用訂單號orderNo做為去重表的唯一索引,每次請求都根據(jù)訂單號向去重表中插入一條數(shù)據(jù)。第一次請求查詢訂單支付狀態(tài),當(dāng)然訂單沒有支付,進(jìn)行支付操作,無論成功與否,執(zhí)行完后更新訂單狀態(tài)為成功或失敗,刪除去重表中的數(shù)據(jù)。后續(xù)的訂單因?yàn)楸碇形ㄒ凰饕迦胧。瑒t返回操作失敗,直到第一次的請求完成(成功或失敗)。 可以看出防重表作用是加鎖的功能。

          分布式鎖

          這里使用的防重表可以使用分布式鎖代替,比如Redis。訂單發(fā)起支付請求,支付系統(tǒng)會(huì)去Redis緩存中查詢是否存在該訂單號的Key,如果不存在,則向Redis增加Key為訂單號。查詢訂單支付已經(jīng)支付,如果沒有則進(jìn)行支付,支付完成后刪除該訂單號的Key。通過Redis做到了分布式鎖,只有這次訂單訂單支付請求完成,下次請求才能進(jìn)來。相比去重表,將放并發(fā)做到了緩存中,較為高效。思路相同, 同一時(shí)間只能完成一次支付請求 。

          token令牌

          這種方式分成兩個(gè)階段:申請token階段和支付階段。

          第一階段,在進(jìn)入到提交訂單頁面之前,需要訂單系統(tǒng)根據(jù)用戶信息向支付系統(tǒng)發(fā)起一次申請token的請求,支付系統(tǒng)將token保存到Redis緩存中,為第二階段支付使用。

          第二階段,訂單系統(tǒng)拿著申請到的token發(fā)起支付請求,支付系統(tǒng)會(huì)檢查Redis中是否存在該token,如果存在,表示第一次發(fā)起支付請求,刪除緩存中token后開始支付邏輯處理;如果緩存中不存在,表示非法請求。

          實(shí)際上這里的token是一個(gè)信物,支付系統(tǒng)根據(jù)token確認(rèn),你是你媽的孩子。不足是需要系統(tǒng)間交互兩次,流程較上述方法復(fù)雜。

          支付緩沖區(qū)

          把訂單的支付請求都快速地接下來,一個(gè)快速接單的緩沖管道。后續(xù)使用異步任務(wù)處理管道中的數(shù)據(jù),過濾掉重復(fù)的待支付訂單。優(yōu)點(diǎn)是同步轉(zhuǎn)異步,高吞吐。不足是不能及時(shí)地返回支付結(jié)果,需要后續(xù)監(jiān)聽支付結(jié)果的異步返回。

          posted on 2016-12-14 13:34 jinfeng_wang 閱讀(181) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 吉安县| 合江县| 炉霍县| 科技| 曲沃县| 黄平县| 若尔盖县| 宁城县| 夏河县| 永济市| 时尚| 绩溪县| 叙永县| 天等县| 黄龙县| 沛县| 农安县| 公安县| 兴业县| 巨鹿县| 山阳县| 错那县| 松潘县| 临邑县| 泰顺县| 山丹县| 乃东县| 禹州市| 南靖县| 竹北市| 会昌县| 陕西省| 霍邱县| 中山市| 尚义县| 奉贤区| 河源市| 隆德县| 海兴县| 香河县| 珲春市|