接口的冪等性原則

接口調(diào)用存在的問題

現(xiàn)如今我們的系統(tǒng)大多拆分為分布式SOA运提,或者微服務(wù),一套系統(tǒng)中包含了多個(gè)子系統(tǒng)服務(wù)癣丧,而一個(gè)子系統(tǒng)服務(wù)往往會(huì)去調(diào)用另一個(gè)服務(wù)栈妆,而服務(wù)調(diào)用服務(wù)無非就是使用RPC通信或者restful,既然是通信掏呼,那么就有可能在服務(wù)器處理完畢后返回結(jié)果的時(shí)候掛掉铅檩,這個(gè)時(shí)候用戶端發(fā)現(xiàn)很久沒有反應(yīng),那么就會(huì)多次點(diǎn)擊按鈕拾给,這樣請(qǐng)求有多次兔沃,那么處理數(shù)據(jù)的結(jié)果是否要統(tǒng)一呢?那是肯定的额衙!尤其在支付場(chǎng)景。

什么是接口冪等性

接口冪等性就是用戶對(duì)于同一操作發(fā)起的一次請(qǐng)求或者多次請(qǐng)求的結(jié)果是一致的县踢,不會(huì)因?yàn)槎啻吸c(diǎn)擊而產(chǎn)生了副作用伟件。舉個(gè)最簡(jiǎn)單的例子,那就是支付斧账,用戶購(gòu)買商品后支付咧织,支付扣款成功,但是返回結(jié)果的時(shí)候網(wǎng)絡(luò)異常索抓,此時(shí)錢已經(jīng)扣了毯炮,用戶再次點(diǎn)擊按鈕,此時(shí)會(huì)進(jìn)行第二次扣款篮幢,返回結(jié)果成功为迈,用戶查詢余額返發(fā)現(xiàn)多扣錢了,流水記錄也變成了兩條...,這就沒有保證接口的冪等性

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

在增刪改查4個(gè)操作中搜锰,尤為注意就是增加或者修改耿战,

A: 查詢操作

查詢對(duì)于結(jié)果是不會(huì)有改變的,查詢一次和查詢多次狈涮,在數(shù)據(jù)不變的情況下鸭栖,查詢結(jié)果是一樣的。select是天然的冪等操作

B: 刪除操作

刪除一次和多次刪除都是把數(shù)據(jù)刪除松却。(注意可能返回結(jié)果不一樣,刪除的數(shù)據(jù)不存在肉渴,返回0带射,刪除的數(shù)據(jù)多條循狰,返回結(jié)果多個(gè),在不考慮返回結(jié)果的情況下,刪除操作也是具有冪等性的)

C: 更新操作

修改在大多場(chǎng)景下結(jié)果一樣,但是如果是增量修改是需要保證冪等性的,如下例子:

把表中id為XXX的記錄的A字段值設(shè)置為1,這種操作不管執(zhí)行多少次都是冪等的

把表中id為XXX的記錄的A字段值增加1,這種操作就不是冪等的

D: 新增操作

增加在重復(fù)提交的場(chǎng)景下會(huì)出現(xiàn)冪等性問題,如以上的支付問題

那么如何設(shè)計(jì)接口才能做到冪等呢绪钥?

常見的兩種實(shí)現(xiàn)方案: 1. 通過代碼邏輯判斷實(shí)現(xiàn) 2. 使用token機(jī)制實(shí)現(xiàn) 下面以支付系統(tǒng)為例,分別對(duì)接口的冪等性進(jìn)行說明與實(shí)現(xiàn)

A: 通過代碼邏輯判斷實(shí)現(xiàn)接口冪等性,只能針對(duì)一些滿足判斷的邏輯實(shí)現(xiàn),具有一定局限性

用戶購(gòu)買商品的訂單系統(tǒng)與支付系統(tǒng);訂單系統(tǒng)負(fù)責(zé)記錄用戶的購(gòu)買記錄已經(jīng)訂單的流轉(zhuǎn)狀態(tài)(orderStatus),支付系統(tǒng)用于付款匣吊,提供如下接口寸潦,訂單系統(tǒng)與支付系統(tǒng)通過分布式網(wǎng)絡(luò)交互。

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

這種情況下斩箫,支付系統(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é)果,往往需要支持接口冪等性谒府。

隨著分布式系統(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 = 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)路徑如圖:

當(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)生修改。

B: 使用token機(jī)制實(shí)現(xiàn)接口冪等性,通用性強(qiáng)的實(shí)現(xiàn)方法

token機(jī)制實(shí)現(xiàn)步驟:1.生成全局唯一的token,token放到redis或jvm內(nèi)存,token會(huì)在頁(yè)面跳轉(zhuǎn)時(shí)獲取.存放到pageScope中,支付請(qǐng)求提交先獲取token2.提交后后臺(tái)校驗(yàn)token推掸,執(zhí)行提交邏輯,提交成功同時(shí)刪除token桶蝎,生成新的token更新redis ,這樣當(dāng)?shù)谝淮翁峤缓髏oken更新了,頁(yè)面再次提交攜帶的token是已刪除的token后臺(tái)驗(yàn)證會(huì)失敗不讓提交 token特點(diǎn):? 要申請(qǐng),一次有效性谅畅,可以限流 注意: redis要用刪除操作來判斷token登渣,刪除成功代表token校驗(yàn)通過,如果用select+delete來校驗(yàn)token毡泻,存在并發(fā)問題胜茧,不建議使用 ? ? ? ? 歡迎工作一到五年的Java工程師朋友們加入Java群:?741514154

群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)牙捉、高性能及分布式竹揍、Jvm性能調(diào)優(yōu)、Spring源碼邪铲,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個(gè)知識(shí)點(diǎn)的架構(gòu)資料)合理利用自己每一分每一秒的時(shí)間來學(xué)習(xí)提升自己无拗,不要再用"沒有時(shí)間“來掩飾自己思想上的懶惰带到!趁年輕,使勁拼英染,給未來的自己一個(gè)交代揽惹!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市四康,隨后出現(xiàn)的幾起案子搪搏,更是在濱河造成了極大的恐慌,老刑警劉巖闪金,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疯溺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡哎垦,警方通過查閱死者的電腦和手機(jī)囱嫩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漏设,“玉大人墨闲,你說我怎么就攤上這事≈?冢” “怎么了鸳碧?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵盾鳞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我瞻离,道長(zhǎng)雁仲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任琐脏,我火速辦了婚禮攒砖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘日裙。我一直安慰自己吹艇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布昂拂。 她就那樣靜靜地躺著受神,像睡著了一般。 火紅的嫁衣襯著肌膚如雪格侯。 梳的紋絲不亂的頭發(fā)上鼻听,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音联四,去河邊找鬼撑碴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛朝墩,可吹牛的內(nèi)容都是我干的醉拓。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼收苏,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼亿卤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鹿霸,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤排吴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后懦鼠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钻哩,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年葛闷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了憋槐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淑趾,死狀恐怖阳仔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤近范,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布嘶摊,位于F島的核電站,受9級(jí)特大地震影響评矩,放射性物質(zhì)發(fā)生泄漏叶堆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一斥杜、第九天 我趴在偏房一處隱蔽的房頂上張望虱颗。 院中可真熱鬧,春花似錦蔗喂、人聲如沸忘渔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)畦粮。三九已至,卻和暖如春乖阵,著一層夾襖步出監(jiān)牢的瞬間宣赔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工瞪浸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留儒将,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓默终,卻偏偏與公主長(zhǎng)得像椅棺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子齐蔽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容