極客時間每日一課筆記
資損的產(chǎn)生有哪幾種場景楷力?
技術(shù)人員如何避免,以及事后哪些解決辦法呢?
對于支付系統(tǒng)的考量赏酥,技術(shù)側(cè)看兩個指標
- 系統(tǒng)能夠提供服務(wù)的穩(wěn)定性
可能的原因是:
cpu飆升導(dǎo)致的服務(wù)響應(yīng)速度慢甸箱,內(nèi)存溢出,線程數(shù)不可控制,以及慢接口導(dǎo)致的連接池不可用等等涂身。
這一點問題最多造成的是用戶體驗不好或者用戶投訴而已
- 系統(tǒng)發(fā)生資金損失
如何來定義資金損失?
在金融支付系統(tǒng)中搓蚪,由于人為操作不當(dāng)或者系統(tǒng)邏輯錯誤導(dǎo)致的資金損失蛤售,這都屬于資金損失。
舉個例子,在一個支付收單系統(tǒng)悴能。中間是支付平臺揣钦,上游商戶系統(tǒng),下游是銀行或第三方系統(tǒng)漠酿。如果支付系統(tǒng)在未明確受到下游支付的成功冯凹,而通知上游支付成功了或者支付失敗,有可能導(dǎo)致上游發(fā)貨和上游再次重置支付炒嘲。一個真實的案例支付系統(tǒng)宇姚,因為對并發(fā)場景的處理不當(dāng)導(dǎo)致重復(fù)支付1000多萬。有人可能會說損失可以通過法律的途徑追回來摸吠,但真實情況是這1000多萬用戶太過于分散空凸,企業(yè)追回的成本太高。
可能發(fā)生資損的場景
支付功能(比如:單筆代收付寸痢、批量代收付呀洲、快捷支付、掃碼支付等)啼止,清結(jié)算功能道逗,提現(xiàn)功能,退款功能献烦,記賬模塊功能等這些環(huán)節(jié)都可能發(fā)生資損滓窍。
常見的問題如下
- 網(wǎng)絡(luò)異常問題
- 查詢和通知問題
- 接口冪等問題
- 狀態(tài)同步問題
- 并發(fā)問題
- 定時器問題
- 提交表單問題
- 重試問題
如何避免和解決資損問題
網(wǎng)絡(luò)異常問題
網(wǎng)絡(luò)異常問題經(jīng)常出現(xiàn)在支付系統(tǒng)每次調(diào)用銀行系統(tǒng)后,接口一旦發(fā)生網(wǎng)絡(luò)異常巩那,這時候支付系統(tǒng)并沒有拿到最終狀態(tài)吏夯,但是商戶側(cè)異常捕獲之后直接定為失敗的終態(tài),其實銀行側(cè)最終狀態(tài)可能是成功即横,但是用戶卻被告知失敗噪生,可能再次發(fā)起重試,這樣就造成了重復(fù)付款东囚。
這種系統(tǒng)調(diào)用的網(wǎng)絡(luò)異常通常包括了
- Connection Reset
- Connection Refused
- Connection Timeout
- The target server failed to respond
- Socket Read Timeout
遇到這一場景的時候跺嗽,把訂單設(shè)置為處理中狀態(tài),然后等待你的定時任務(wù)主動查詢或者支付回調(diào)再次通知页藻,達到最終狀態(tài)即可桨嫁。
查詢和通知問題
支付接口一般包括了交易接口,主動查詢接口和異步通知接口份帐。
常見的問題有以下四種
- 查詢失敗或者異常
訂單結(jié)果查詢失敗或者異常并不代表這筆訂單最終是失敗璃吧,特別是第三方查詢交易接口返回的響應(yīng)碼狀態(tài)
就是失敗,這時候開發(fā)人員一定要明確這是訂單查詢操作本身失敗并不是交易失敗废境。如果這時候粗心(沒認真看文檔)導(dǎo)致把訂單的最終結(jié)果設(shè)置失敗肚逸,那么就可能引發(fā)重復(fù)支付的問題爷辙。
- 查詢頻率過快
為了保證支付訂單能夠最快的到支付結(jié)果,我們會在系統(tǒng)的交易請求之后朦促,比如說快速的15秒之內(nèi)發(fā)起主動查詢膝晾,那結(jié)果呢,第三方系統(tǒng)返回?zé)o此訂單务冕,同時支付平臺返回也無此清單給商戶血当,這時候?qū)е律虘粝到y(tǒng)又發(fā)生了第二次的支付請求,實際上這種問題經(jīng)過最后的排查得知禀忆,第三方系統(tǒng)由于處理請求的鏈路比較長臊旭,只是說不允許那么快發(fā)起交易,建議在兩分鐘之后再來查詢箩退,如果兩分鐘之后查詢再返回上有無此訂單离熏,雖然說這種問題非常極端,但是我們也是會遇到.
- 被查詢接口冪等性問題
案例
T日的訂單已經(jīng)有了最終狀態(tài)戴涝,因為某種特殊的業(yè)務(wù)場景滋戳,運營人員手工操作,把這筆訂單從最終狀態(tài)修改成了處理中啥刻,因為人員認為系統(tǒng)有查詢接口可以再次查回來奸鸯,結(jié)果實際情況是什么呢?銀行系統(tǒng)T+1日返回了不一樣的訂單狀態(tài)可帽,結(jié)果導(dǎo)致這個商戶接受這個錯誤狀態(tài)之后又發(fā)起了再次支付娄涩,是因為銀行在T+1日,這筆交易發(fā)生了撤回失敗映跟,返回的狀態(tài)是想告訴大家蓄拣,這是撤回失敗的狀態(tài),由于這樣導(dǎo)致了狀態(tài)不一致努隙。但這個故事在運營商的同學(xué)理解來看球恤,銀行的接口應(yīng)該每次查詢都返回同樣的狀態(tài)才對啊。
其實這個東西從本質(zhì)來說剃法,也可以是一個接口冪等性的問題,對于這個問題建議的方案是說如果我們沒有辦法保證別人系統(tǒng)兼容性的問題路鹰,那我們可以保證我們的系統(tǒng)兼容性贷洲。
- 異步通知
我們經(jīng)常會遇到上游或者下游重復(fù)通知的問題,并且甚至有可能遇到前后兩次通知不一致的情況晋柱,這種東西在某些支付公司的接口中可能遇到過优构,這種極少的情況也不能忽略,建議處理方式是第一次通知的結(jié)果是準確的雁竞,一定會以第一次結(jié)果為準钦椭,如果第二次通知發(fā)生結(jié)果規(guī)則情況這時候還是以第一次為主拧额,那這個狀態(tài)呢進行預(yù)警人為干預(yù),否則的話那么就容易發(fā)生重復(fù)發(fā)起的情況彪腔。
- 下單接口冪等性
只有做到冪等方才可進行后期的重試侥锦。對于處理重復(fù)提交的情況辦法是上游訂單流水號作為唯一冪等條件。比如說在數(shù)據(jù)庫訂單號重復(fù)的時候拋出異常德挣,再去查詢那個之前的支付結(jié)果恭垦。除此之外,還可以在請求的入口處用 redis 防重復(fù)格嗅。
狀態(tài)同步問題
前面講過番挺,支付系統(tǒng)最終狀態(tài)完全依賴于下游系統(tǒng)。但是每家系統(tǒng)返回的報文都有差異屯掖,有些系統(tǒng)則響應(yīng)碼和詳細信息比較復(fù)雜玄柏,在我以往的經(jīng)驗中,根據(jù)訂單狀態(tài)及時響應(yīng)碼是一件非常重要的事情贴铜,總結(jié)一下最佳實踐粪摘。
對于查詢接口返回訂單不存在的情況,需要單獨設(shè)置錯誤碼做特殊報警處理阀湿,付款類的交易不可以設(shè)置為失敗狀態(tài)赶熟,這樣才可以避免資金的重復(fù)支付。
資金類的交易訂單設(shè)置的狀態(tài)是根據(jù)第三方報文來設(shè)置的陷嘴,更新狀態(tài)需要采用保守的策略映砖,對于不確定的狀態(tài)不可以直接就用他的失敗。這樣也可以避免資金的重復(fù)支付灾挨。
硬件服務(wù)器年久失修邑退,導(dǎo)致崩潰有時候 redis,mq劳澄,正在處理中的瞬時的內(nèi)存狀態(tài)就會丟掉服務(wù)器的啟動之后地技,狀態(tài)如果處理不當(dāng),也會導(dǎo)致再次重復(fù)處理秒拔。
并發(fā)問題
- 集群環(huán)境下兩個同樣的服務(wù)并發(fā)導(dǎo)致的
- 機器人何為操作導(dǎo)致并發(fā)
- 定時任務(wù)并發(fā)執(zhí)行的
表單重復(fù)提交問題
比如用戶連續(xù)點擊了兩次取消按鈕或者用戶提交表單的時候莫矗。因為網(wǎng)速過慢,又點擊了一次砂缩,這樣的并發(fā)都會導(dǎo)致重復(fù)提交問題作谚。
定時器重復(fù)執(zhí)行
定時任務(wù)一般會和項目工程在一起,容易被部署在多個服務(wù)器集群累庵芭∶美粒或者遇到過定時器浪打浪的問題,比如說我的定時器兩分鐘執(zhí)行一次双吆,由于某種異常眨唬,比如說線程阻塞連接超時響應(yīng)超時等会前,第一輪定時器還沒有執(zhí)行完成,第二輪定時器啟動之后匾竿,那就重復(fù)執(zhí)行了這種問題瓦宜。需要控制定時任務(wù)的狀態(tài),或者使用狀態(tài)機來控制所處理的訂單狀態(tài)搂橙。
各種重試機制問題
很多情況還是中間件出現(xiàn)的各種重試導(dǎo)致的歉提,如果開發(fā)人員不明白里面的原來很難排查出來。比如 http 的重試区转,各種中間件的retry機制都會導(dǎo)致訂單重復(fù)支付苔巨。
如何防重
前端大概就是,有 Token 校驗废离,js 禁用提交按鈕等等
主要是后端 這里有這樣子一個方案 數(shù)據(jù)庫樂觀鎖侄泽,有限狀態(tài)機,白名單
有限狀態(tài)機即又稱為自動機簡稱狀態(tài)機蜻韭,表示有限個狀態(tài)悼尾,在這些狀態(tài)之間的轉(zhuǎn)移和動作等行為的數(shù)學(xué)模型。
這里的重點是狀態(tài)的管理和狀態(tài)的驅(qū)動肖方,在支付系統(tǒng)中狀態(tài)的流轉(zhuǎn)的控制可以避免訂單被錯誤的執(zhí)行和重復(fù)執(zhí)行闺魏。
比如成功的狀態(tài)不允許被變更為處理中,已發(fā)送的訂單狀態(tài)不允許被撿起支付等俯画。
具體的解決方案如下析桥,如果當(dāng)前的訂單處于路由成功的狀態(tài),那我們?yōu)榱吮苊舛〞r任務(wù)和人工并發(fā)執(zhí)行艰垂,只需要如圖這樣的一段非常簡單的代碼泡仗,我們來解析下這段代碼在這段代碼里。數(shù)據(jù)庫樂觀鎖來控制并發(fā)白
數(shù)據(jù)庫樂觀鎖來控制并發(fā)白名單猜憎,控制狀態(tài)機的流轉(zhuǎn)必須是白名單機制娩怎,不可以是黑名單機制。白名單告訴我們胰柑,只有15的狀態(tài)才會允許更新為63或者60截亦,否則更新失敗。
其他風(fēng)險
- 運營人員產(chǎn)線配置
- 運維人員修改系統(tǒng)基礎(chǔ)參數(shù)
機制流程柬讨,實時監(jiān)控崩瓤,總之來說事前避免,事中監(jiān)控姐浮,墨菲定律告訴我們該發(fā)生的是比會發(fā)生谷遂,所以還需要做到事后止損葬馋。
本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布卖鲤!