接口幂等设计探索实践
冪等性原本是數(shù)學上的概念,即使公式:f(x)=f(f(x)) 能夠成立的數(shù)學性質(zhì)。用在編程領域,則意為對同一個系統(tǒng),使用同樣的條件,一次請求和重復的多次請求對系統(tǒng)資源的影響是一致的、或者說是符合預期的。
背景
穩(wěn)定性設計第一篇:這一小節(jié)開始講設計系統(tǒng)穩(wěn)定性保證的相關設計,誰都不想自己負責的系統(tǒng)三天兩頭就出故障,也不想周六日跟女票葡萄美酒夜光杯的時候一個電話call去VPN辦公,那么你就想辦法讓你的系統(tǒng)盡量穩(wěn)定,我們的
阿里新零售和阿里媽媽,美團,過去我面這些公司都被問過接口冪等相關,接口冪等設計在分布式系統(tǒng)開發(fā)中非常常見且很重要,后來我自己做面試官也慢慢意識到冪等的重要性。
一些初學者對冪等這個概念完全不理解,更不知道如何設計,這在工作中很容易給自己惹麻煩,所以一定要會!一定要會!一定要會!
冪等與重復請求區(qū)別
冪等:多次請求,在第一次請求不知道結果(比如超時)或者失敗的異常情況下,發(fā)起多次請求,但卻不會因多次請求而出現(xiàn)數(shù)據(jù)變化。
重復請求:多次請求,第一次請求已經(jīng)成功的前提下,后續(xù)多次進行請求,如果后端服務非冪等服務,則每次請求均會進行數(shù)據(jù)改變;如果后端是冪等服務,則每次請求返回數(shù)據(jù)不會變化。
系統(tǒng)里有哪些接口使用到了冪等設計?
問題分析:
冪等的概念首先你肯定理解了,簡單通俗易懂,就是無論你是 Http 接口還是 RPC 接口,入?yún)⒉蛔兊那闆r下,無論請求多少次,結果都是一樣的,請求結果不會因為請求次數(shù)不同而改變,沒有任務副作用。
我:
我參加工作的第一年,在某在線購票(電影票)App的一家公司做后臺系統(tǒng)開發(fā),當時我負責積分系統(tǒng),工作中接到這樣一個線上活動需求。業(yè)務場景描述:用戶每天使用 App 點擊簽到按鈕參加活動,領取相應的積分,每個用戶每天只能參加一次簽到領積分活動,簽到按鈕在點擊一次后會自設置灰色變?yōu)椴豢牲c擊的狀態(tài),這個領積分的接口由我負責開發(fā),提供 API 給 客戶端同事,上線后出現(xiàn)這樣一個bug,當時沒有完善的業(yè)務監(jiān)控系統(tǒng),功能上線后第二天問出于好奇系統(tǒng)里積分最高的人有多少積分,就在后臺跑了一個sql,這一好奇,驚奇的發(fā)現(xiàn)有的用戶積分高達幾萬分,因為積分除了簽到領取外,大多都是消費累計積分,一塊錢才能累積一分,我表示懷疑,什么能人看電影能看幾萬塊錢?
帶著這個疑問,我查詢了他的積分累積記錄,發(fā)現(xiàn)大部分積分都是靠簽到領積分獲得的,按照活動規(guī)則,一個人一天只能參加一次簽到,不可能有這么多積分,而這個用戶一天簽到幾百次,后來經(jīng)過和前端一同檢查bug發(fā)現(xiàn)問題所在,原因是簽到按鈕雖然變灰,但是請求的 url 沒有在前端頁面隱藏,用戶通過技術手段繞過 button 變灰的前端限制重復刷新了接口,重復獲得積分。
事后問題分析:
這個bug最大問題還在我這里,因為我的接口沒有做冪等設計,正確的邏輯應該是根據(jù)系統(tǒng)當前日期做冪等,冪等后無論用戶發(fā)起多少次請求,最后的結果都是一樣的,積分只累加一次。好在這個bug沒有被黑產(chǎn)發(fā)現(xiàn),只有幾個用戶發(fā)現(xiàn)損失可控。
因為我缺少設計經(jīng)驗,不懂冪等設計,領導也沒提醒我,所以出現(xiàn)這種bug,經(jīng)歷更多和錢相關的系統(tǒng)開發(fā)后,我明白一個道理,任何系統(tǒng)設計,都要考慮業(yè)務的安全性,內(nèi)部系統(tǒng)可以為了節(jié)省人力,適當簡化設計,做到防君子不防小人,假設你的同事都是君子,對C端用戶的系統(tǒng),不光要防君子,還要防小人,風險防范不能全指望風控系統(tǒng),有時bug可能會來自系統(tǒng)內(nèi)部,比如用戶并沒有惡意盜刷之意,只是網(wǎng)絡不好,用戶等了兩秒鐘還沒加載完就多點了幾次簽到按鈕,我的接口沒有做冪等設計,只要收到請求就會多給用戶加積分,這個時候能怪用戶嗎?很顯然是開發(fā)者的責任。
關于這個接口的冪等設計,我是這樣解決的:
1.積分接口后臺根據(jù)用戶手機號 + userId + 系統(tǒng)當前日期拼接后生成唯一流水號,根據(jù)流水號后保存,如果用戶重復發(fā)起請求,先根據(jù)唯一流水號校驗在后臺做校驗,如果流水號存在直接返回上一次請求結果,考慮到并發(fā)的情況下,狀態(tài)判斷使用了鎖處理。2.開發(fā)業(yè)務監(jiān)控系統(tǒng),采用定時任務每天生成系統(tǒng)里 Top100 積分增長最多名單,運營 or 技術人員每天觀察有沒有異常。
經(jīng)過這次bug反思,學習到亮點:
1.理解冪等設計的重要性,凡事和錢相關的功能請謹慎。2.監(jiān)控系統(tǒng)的重要性,這里的監(jiān)控說的是業(yè)務類監(jiān)控,如果那天我沒有好奇系統(tǒng)里誰的積分最高,這個bug會什么時候發(fā)現(xiàn)?
面試官:嚯,有點意思,你還真的是寫了個大bug,弄懂了吸取教訓就好,可別進了我的項目組后拿我們的系統(tǒng)寫這bug。
深入分析:
在編程中一個冪等操作的特點是其任意多次執(zhí)行所產(chǎn)生的影響均與一次執(zhí)行的影響相同。冪等函數(shù),或冪等方法,是指可以使用相同參數(shù)重復執(zhí)行,并能獲得相同結果的函數(shù)。這些函數(shù)不會影響系統(tǒng)狀態(tài),也不用擔心重復執(zhí)行會對系統(tǒng)造成改變。例如,“setTrue()”函數(shù)就是一個冪等函數(shù),無論多次執(zhí)行,其結果都是一樣的.更復雜的操作冪等保證是利用唯一交易號(流水號)實現(xiàn)。
—— 百度百科
如果你了解 Restful 風格接口,相信你對 GET / POST / DELETE 幾個動詞不陌生,小編在一次面試中,我記得是錘子科技,面試官問我是否了解 Rest 接口,我balabala回答了這幾常用的動詞,面試官又問我:那你除了知道 GET 是從服務器獲取資源,還有別的理解嗎?當時我沒搭上來,出了公司以后才想起,GET 動作的設計應該是冪等的。同理 DELETE 也是冪等的,如果你設計的接口 GET / DELETE 不是冪等的,那么你可能要重新思考一下了。
1.工作中常見的冪等設計場景
如果你做的功能和錢相關,或者能還錢的,那么你就要小心了,每一個接口都要先考慮下是否需要冪等設計,下面是兩種常見的需求場景。
1.發(fā)券/積分接口,通常通過 orderId userId 做冪等校驗。2.支付/退款接口,我們不希望用戶發(fā)起多次支付都收到用戶的錢,用戶會投訴,還要把錢退還給用戶,對系統(tǒng)還是客服人員來說都是無用功,支付系統(tǒng)非常復雜,想做好支付系統(tǒng),還有很多東西需要學習,要考慮網(wǎng)絡延遲,服務異常,訂單中心回掉超時等各種不穩(wěn)定的因素,通常采用前端控制,邏輯層狀態(tài)的控制,數(shù)據(jù)層唯一索引的控制,以及分布式鎖的控制,在冪等篇不過過多討論。
2.冪等接口常見設計方案
1.客戶端按鈕提交限制,每次提交一個請求時,按鈕置為不可用。2.后臺系統(tǒng)邏輯層處理,生成保存唯一ID(流水號),每次請求先校驗流水號是否已經(jīng)存在,存在則表示重復操作,直接返回上一次操作結果。3.token校驗機制,客戶端請求前先申請token,同一個token只處理一次,無token或者相同token不做處理4.分布式鎖,如引入 Redis 分布式鎖,防止其他請求重復操作。5.請求隊列,引入 MQ 排隊的方式讓請求有序處理,關于異步操作的應用會在后面的章節(jié)講解。
每一種方案都有自己的優(yōu)缺點,比如客服端按鈕提交限制,實現(xiàn)簡單,但是不能同根本上解決問題,后臺生成唯一ID,判斷存在狀態(tài)必須要保證原子操作,可以采用多種方案組合的方式解決冪等問題,我們的目標是,用最容易維護的方法解決問題。
總結
在過去的工作經(jīng)歷中,我招進來一個工作三年的同事,場景是開發(fā)一個退款接口,review代碼的時候,我發(fā)現(xiàn)退款的功能是做完了,錢確實能退,但是并沒有做冪等設計,我倆討論了下,我說:如果同一個訂單被請求了兩次退款,那這錢是不是要退兩次,這很危險呀?當時這個同事并沒有意識到這一點,因為沒有相關經(jīng)驗,連概念都不知道,作為一個三年經(jīng)驗的實在不應該,和錢相關的功能一定要慎重,做冪等設計就是為了系統(tǒng)能防君子,也要防小人。
總結
以上是生活随笔為你收集整理的接口幂等设计探索实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 别在.NET死忠粉面前黑.NET5,它未
- 下一篇: WebBenchmark动态测试Weba