極客時間每日一課筆記
資損的產(chǎn)生有哪幾種場景?
技術人員如何避免,以及事后哪些解決辦法呢?
對于支付系統(tǒng)的考量,技術側看兩個指標
- 系統(tǒng)能夠提供服務的穩(wěn)定性
可能的原因是:
cpu飆升導致的服務響應速度慢,內存溢出,線程數(shù)不可控制,以及慢接口導致的連接池不可用等等。
這一點問題最多造成的是用戶體驗不好或者用戶投訴而已
- 系統(tǒng)發(fā)生資金損失
如何來定義資金損失?
在金融支付系統(tǒng)中,由于人為操作不當或者系統(tǒng)邏輯錯誤導致的資金損失,這都屬于資金損失。

舉個例子,在一個支付收單系統(tǒng)。中間是支付平臺,上游商戶系統(tǒng),下游是銀行或第三方系統(tǒng)。如果支付系統(tǒng)在未明確受到下游支付的成功,而通知上游支付成功了或者支付失敗,有可能導致上游發(fā)貨和上游再次重置支付。一個真實的案例支付系統(tǒng),因為對并發(fā)場景的處理不當導致重復支付1000多萬。有人可能會說損失可以通過法律的途徑追回來,但真實情況是這1000多萬用戶太過于分散,企業(yè)追回的成本太高。
可能發(fā)生資損的場景
支付功能(比如:單筆代收付、批量代收付、快捷支付、掃碼支付等),清結算功能,提現(xiàn)功能,退款功能,記賬模塊功能等這些環(huán)節(jié)都可能發(fā)生資損。
常見的問題如下
- 網(wǎng)絡異常問題
- 查詢和通知問題
- 接口冪等問題
- 狀態(tài)同步問題
- 并發(fā)問題
- 定時器問題
- 提交表單問題
- 重試問題
如何避免和解決資損問題
網(wǎng)絡異常問題
網(wǎng)絡異常問題經(jīng)常出現(xiàn)在支付系統(tǒng)每次調用銀行系統(tǒng)后,接口一旦發(fā)生網(wǎng)絡異常,這時候支付系統(tǒng)并沒有拿到最終狀態(tài),但是商戶側異常捕獲之后直接定為失敗的終態(tài),其實銀行側最終狀態(tài)可能是成功,但是用戶卻被告知失敗,可能再次發(fā)起重試,這樣就造成了重復付款。
這種系統(tǒng)調用的網(wǎng)絡異常通常包括了
- Connection Reset
- Connection Refused
- Connection Timeout
- The target server failed to respond
- Socket Read Timeout
遇到這一場景的時候,把訂單設置為處理中狀態(tài),然后等待你的定時任務主動查詢或者支付回調再次通知,達到最終狀態(tài)即可。
查詢和通知問題
支付接口一般包括了交易接口,主動查詢接口和異步通知接口。
常見的問題有以下四種
- 查詢失敗或者異常
訂單結果查詢失敗或者異常并不代表這筆訂單最終是失敗,特別是第三方查詢交易接口返回的響應碼狀態(tài)就是失敗,這時候開發(fā)人員一定要明確這是訂單查詢操作本身失敗并不是交易失敗。如果這時候粗心(沒認真看文檔)導致把訂單的最終結果設置失敗,那么就可能引發(fā)重復支付的問題。
- 查詢頻率過快
為了保證支付訂單能夠最快的到支付結果,我們會在系統(tǒng)的交易請求之后,比如說快速的15秒之內發(fā)起主動查詢,那結果呢,第三方系統(tǒng)返回無此訂單,同時支付平臺返回也無此清單給商戶,這時候導致商戶系統(tǒng)又發(fā)生了第二次的支付請求,實際上這種問題經(jīng)過最后的排查得知,第三方系統(tǒng)由于處理請求的鏈路比較長,只是說不允許那么快發(fā)起交易,建議在兩分鐘之后再來查詢,如果兩分鐘之后查詢再返回上有無此訂單,雖然說這種問題非常極端,但是我們也是會遇到.
- 被查詢接口冪等性問題
案例
T日的訂單已經(jīng)有了最終狀態(tài),因為某種特殊的業(yè)務場景,運營人員手工操作,把這筆訂單從最終狀態(tài)修改成了處理中,因為人員認為系統(tǒng)有查詢接口可以再次查回來,結果實際情況是什么呢?銀行系統(tǒng)T+1日返回了不一樣的訂單狀態(tài),結果導致這個商戶接受這個錯誤狀態(tài)之后又發(fā)起了再次支付,是因為銀行在T+1日,這筆交易發(fā)生了撤回失敗,返回的狀態(tài)是想告訴大家,這是撤回失敗的狀態(tài),由于這樣導致了狀態(tài)不一致。但這個故事在運營商的同學理解來看,銀行的接口應該每次查詢都返回同樣的狀態(tài)才對啊。
其實這個東西從本質來說,也可以是一個接口冪等性的問題,對于這個問題建議的方案是說如果我們沒有辦法保證別人系統(tǒng)兼容性的問題,那我們可以保證我們的系統(tǒng)兼容性。
- 異步通知
我們經(jīng)常會遇到上游或者下游重復通知的問題,并且甚至有可能遇到前后兩次通知不一致的情況,這種東西在某些支付公司的接口中可能遇到過,這種極少的情況也不能忽略,建議處理方式是第一次通知的結果是準確的,一定會以第一次結果為準,如果第二次通知發(fā)生結果規(guī)則情況這時候還是以第一次為主,那這個狀態(tài)呢進行預警人為干預,否則的話那么就容易發(fā)生重復發(fā)起的情況。
- 下單接口冪等性
只有做到冪等方才可進行后期的重試。對于處理重復提交的情況辦法是上游訂單流水號作為唯一冪等條件。比如說在數(shù)據(jù)庫訂單號重復的時候拋出異常,再去查詢那個之前的支付結果。除此之外,還可以在請求的入口處用 redis 防重復。
狀態(tài)同步問題
前面講過,支付系統(tǒng)最終狀態(tài)完全依賴于下游系統(tǒng)。但是每家系統(tǒng)返回的報文都有差異,有些系統(tǒng)則響應碼和詳細信息比較復雜,在我以往的經(jīng)驗中,根據(jù)訂單狀態(tài)及時響應碼是一件非常重要的事情,總結一下最佳實踐。
對于查詢接口返回訂單不存在的情況,需要單獨設置錯誤碼做特殊報警處理,付款類的交易不可以設置為失敗狀態(tài),這樣才可以避免資金的重復支付。
資金類的交易訂單設置的狀態(tài)是根據(jù)第三方報文來設置的,更新狀態(tài)需要采用保守的策略,對于不確定的狀態(tài)不可以直接就用他的失敗。這樣也可以避免資金的重復支付。
硬件服務器年久失修,導致崩潰有時候 redis,mq,正在處理中的瞬時的內存狀態(tài)就會丟掉服務器的啟動之后,狀態(tài)如果處理不當,也會導致再次重復處理。
并發(fā)問題
- 集群環(huán)境下兩個同樣的服務并發(fā)導致的
- 機器人何為操作導致并發(fā)
- 定時任務并發(fā)執(zhí)行的
表單重復提交問題
比如用戶連續(xù)點擊了兩次取消按鈕或者用戶提交表單的時候。因為網(wǎng)速過慢,又點擊了一次,這樣的并發(fā)都會導致重復提交問題。
定時器重復執(zhí)行
定時任務一般會和項目工程在一起,容易被部署在多個服務器集群累?;蛘哂龅竭^定時器浪打浪的問題,比如說我的定時器兩分鐘執(zhí)行一次,由于某種異常,比如說線程阻塞連接超時響應超時等,第一輪定時器還沒有執(zhí)行完成,第二輪定時器啟動之后,那就重復執(zhí)行了這種問題。需要控制定時任務的狀態(tài),或者使用狀態(tài)機來控制所處理的訂單狀態(tài)。
各種重試機制問題
很多情況還是中間件出現(xiàn)的各種重試導致的,如果開發(fā)人員不明白里面的原來很難排查出來。比如 http 的重試,各種中間件的retry機制都會導致訂單重復支付。
如何防重
前端大概就是,有 Token 校驗,js 禁用提交按鈕等等
主要是后端 這里有這樣子一個方案 數(shù)據(jù)庫樂觀鎖,有限狀態(tài)機,白名單
有限狀態(tài)機即又稱為自動機簡稱狀態(tài)機,表示有限個狀態(tài),在這些狀態(tài)之間的轉移和動作等行為的數(shù)學模型。

這里的重點是狀態(tài)的管理和狀態(tài)的驅動,在支付系統(tǒng)中狀態(tài)的流轉的控制可以避免訂單被錯誤的執(zhí)行和重復執(zhí)行。
比如成功的狀態(tài)不允許被變更為處理中,已發(fā)送的訂單狀態(tài)不允許被撿起支付等。
具體的解決方案如下,如果當前的訂單處于路由成功的狀態(tài),那我們?yōu)榱吮苊舛〞r任務和人工并發(fā)執(zhí)行,只需要如圖這樣的一段非常簡單的代碼,我們來解析下這段代碼在這段代碼里。數(shù)據(jù)庫樂觀鎖來控制并發(fā)白

數(shù)據(jù)庫樂觀鎖來控制并發(fā)白名單,控制狀態(tài)機的流轉必須是白名單機制,不可以是黑名單機制。白名單告訴我們,只有15的狀態(tài)才會允許更新為63或者60,否則更新失敗。
其他風險
- 運營人員產(chǎn)線配置
- 運維人員修改系統(tǒng)基礎參數(shù)
機制流程,實時監(jiān)控,總之來說事前避免,事中監(jiān)控,墨菲定律告訴我們該發(fā)生的是比會發(fā)生,所以還需要做到事后止損。
本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布!