轉(zhuǎn)轉(zhuǎn)支付通道監(jiān)控系統(tǒng)的搭建
1 背景介紹
為了滿足不斷增長的業(yè)務(wù)需求,轉(zhuǎn)轉(zhuǎn)逐步接入了大量的支付通道,而第三方系統(tǒng)的穩(wěn)定性參差不齊,通道故障時(shí)有發(fā)生。當(dāng)三方通道發(fā)生異常時(shí)我們的感知比較后置,比如大量的系統(tǒng)告警,甚至需要等業(yè)務(wù)或用戶反饋才能感知到異常。作為承接全公司支付業(yè)務(wù)的核心系統(tǒng),想要建立一個(gè)能給上游提供穩(wěn)定服務(wù)的系統(tǒng),僅依靠人工維護(hù)是遠(yuǎn)遠(yuǎn)不夠的,因此建立一個(gè)完善的支付通道自動(dòng)化管理系統(tǒng)就提上了日程。
2 設(shè)計(jì)目標(biāo)
結(jié)合轉(zhuǎn)轉(zhuǎn)自身業(yè)務(wù)的特點(diǎn),我們整理了支付通道自動(dòng)化管理系統(tǒng)重點(diǎn)需要解決的問題:
- 多通道、多主體的通道監(jiān)控能力;
- 故障快速發(fā)現(xiàn),快速定位異常原因;
- 盡量做到無誤報(bào)、無漏報(bào);
- 通道故障自動(dòng)化切換的能力;
3 技術(shù)選型
基于以上背景,再來看下技術(shù)方案的選型:
3.1 熔斷器
提到故障的熔斷和降級(jí),首先想到的是市面上成熟的組件是否能夠滿足,比如 Hystrix,結(jié)合轉(zhuǎn)轉(zhuǎn)當(dāng)前的業(yè)務(wù)場景來看,有以下幾點(diǎn)無法滿足訴求:
- 熔斷器的降級(jí)熔斷是基于接口的,無法滿足通道和商戶號(hào)維度的降級(jí);
- 流量回切時(shí)可能異常仍未恢復(fù),無法自定義探測流量的范圍,比如回切時(shí)指定用戶或業(yè)務(wù),容易造成二次事故;
3.2 時(shí)序數(shù)據(jù)庫
熔斷器無法滿足目標(biāo)后,我們就將目標(biāo)轉(zhuǎn)向了自研,首先想做一個(gè)監(jiān)控系統(tǒng),底層一般都是選用時(shí)序數(shù)據(jù)庫來存儲(chǔ),以下是熱門時(shí)序數(shù)據(jù)庫的排名:
時(shí)序數(shù)據(jù)庫排名
結(jié)合轉(zhuǎn)轉(zhuǎn)目前的現(xiàn)狀,我們最終將范圍鎖定在了 Prometheus 和基于 Redis 自研:
準(zhǔn)確性方面: 由于 Prometheus 在設(shè)計(jì)上就放棄了一部分?jǐn)?shù)據(jù)準(zhǔn)確性,放棄一點(diǎn)準(zhǔn)確性得到的是更高的可靠性,架構(gòu)簡單、數(shù)據(jù)簡單、運(yùn)維簡單、節(jié)約機(jī)器成本與人力成本。
通常對(duì)于監(jiān)控系統(tǒng),數(shù)據(jù)擁有少量的誤差是可以接受的,而對(duì)于自動(dòng)切換通道這種高敏感場景并不適用:
- 比如在兩次采樣的間隔 (15s) 中有一個(gè)瞬時(shí)小尖峰,那么這次小尖峰是觀察不到的
- 再比如 QPS、RT、P95、P99 這些值都是估算值,無法和日志、數(shù)據(jù)庫一樣做到 100% 準(zhǔn)確
Prometheus 不適合的場景
接入和學(xué)習(xí)成本方面: Prometheus 對(duì)于業(yè)務(wù)研發(fā)來講還是有一定的學(xué)習(xí)成本的,也不便于后期的維護(hù),而 Redis 對(duì)于 Java 后端開發(fā)者來說再熟悉不過了,無論是前期的學(xué)習(xí)成本還是后期的維護(hù)成本都比較低。
結(jié)合以上幾個(gè)方面考量,最終決定基于 Redis 自研 “時(shí)序數(shù)據(jù)庫” 來滿足當(dāng)前的訴求。
4 架構(gòu)設(shè)計(jì)
架構(gòu)設(shè)計(jì)
收款和付款時(shí)會(huì)先通過各自的通道路由,篩選出可用的支付通道列表,獲取到通道之后調(diào)用網(wǎng)關(guān)下單或打款,再由網(wǎng)關(guān)來向三方發(fā)起請(qǐng)求,請(qǐng)求結(jié)束后將三方返回的結(jié)果通過 MQ 上報(bào)到通道監(jiān)控系統(tǒng)。
監(jiān)控系統(tǒng)在監(jiān)聽到消息后,將監(jiān)控的數(shù)據(jù)存儲(chǔ)到 Redis,再由數(shù)據(jù)計(jì)算模塊拉取 Redis 的數(shù)據(jù)進(jìn)行篩選過濾后,匯總計(jì)算各通道的失敗率,最后根據(jù)各通道配置的告警規(guī)則觸發(fā)通道異常告警。
Redis 中的數(shù)據(jù)會(huì)定期向 MySQL 備份,以便后續(xù)故障分析使用,同時(shí)會(huì)有離線任務(wù)定時(shí)清理 Redis 中的數(shù)據(jù),避免 Redis 中存儲(chǔ)的數(shù)據(jù)量過大。
同時(shí)為了更直觀的觀察各通道數(shù)據(jù)指標(biāo)的變化,將收集到的數(shù)據(jù)指標(biāo)上報(bào)到了 Prometheus,通過 Grafana 數(shù)據(jù)看板來觀察通道健康度。
通道指標(biāo)看板
通道自動(dòng)上下線是比較敏感的操作,嚴(yán)格依賴算法的準(zhǔn)確性,所以系統(tǒng)上線初期,我們只上線了手動(dòng)上下線的能力,需要在收集大量樣本后,不斷完善算法,提高監(jiān)控的準(zhǔn)確性和靈敏度,再逐步切換至基于監(jiān)控的通道自動(dòng)化管理。
5 實(shí)現(xiàn)細(xì)節(jié)
5.1 數(shù)據(jù)結(jié)構(gòu)
再來看下基于 Redis 的數(shù)據(jù)存儲(chǔ)是如何存儲(chǔ)的,雖然沒有使用時(shí)序數(shù)據(jù)庫,但是在數(shù)據(jù)結(jié)構(gòu)選擇上也是結(jié)合了時(shí)序數(shù)據(jù)庫的存儲(chǔ)思想來設(shè)計(jì)的,下面就以最熱門的 InfluxDB 來對(duì)比看下:
InfluxDB | Redis |
tags 標(biāo)簽 | set 記錄監(jiān)控維度 |
time 時(shí)間戳 | zset 存儲(chǔ)時(shí)間戳(秒) |
fields 數(shù)據(jù) | hash 存儲(chǔ)具體的值 |
- tags 標(biāo)簽:記錄監(jiān)控的維度,相對(duì)應(yīng)的在 Redis 中選用的是 set 來存儲(chǔ),利用 set 去重的特性,剛好可以記錄需要監(jiān)控的指標(biāo)。
- tims 時(shí)間:記錄發(fā)生的時(shí)間,相對(duì)應(yīng)的在 Redis 中選用的是 zset 來存儲(chǔ),在監(jiān)控時(shí)需要根據(jù)時(shí)間范圍進(jìn)行查找,且要求是按照時(shí)間排序的,剛好可以利用 zset 的按照 score 來排序和查找的特性,用于記錄監(jiān)控點(diǎn)位的時(shí)間戳,為了避免數(shù)據(jù)量過大,這里記錄的單位是秒,也就一秒一個(gè)點(diǎn)位。
- fields 數(shù)據(jù):存儲(chǔ)具體的監(jiān)控?cái)?shù)據(jù),相對(duì)應(yīng)的在 Redis 中選用的是 hash 來存儲(chǔ),利用 hash 中存儲(chǔ) key、value 的特性,來記錄請(qǐng)求結(jié)果數(shù)據(jù),記錄一個(gè)點(diǎn)位內(nèi)(1 秒)的成功與失敗的情況,并記錄失敗的具體原因,key 用來存儲(chǔ)請(qǐng)求結(jié)果,value 用來記錄對(duì)應(yīng)結(jié)果 1 秒內(nèi)發(fā)生的次數(shù)。
最終 Redis 中存儲(chǔ)的結(jié)構(gòu)樣例如下:
1.set
存儲(chǔ)已統(tǒng)計(jì)的維度,具體到商戶號(hào)
key: routeAlarm:alarmitems
value: 微信-打款-100000111
微信-打款-100000112
微信-打款-100000113
.......
2.zset
存儲(chǔ)指定商戶號(hào)請(qǐng)求的時(shí)間戳(秒),同一秒的數(shù)據(jù)會(huì)覆蓋存儲(chǔ)
key: routeAlarm:alarmitem:timeStore:微信-打款-100000111
score: 1657164225 value: 1657164225
score: 1657164226 value: 1657164226
score: 1657164227 value: 1657164227
.......
3.hash
存儲(chǔ)指定商戶號(hào)1秒內(nèi)的請(qǐng)求結(jié)果, 每秒?yún)R總一份結(jié)果
key: routeAlarm:alarmitem:fieldStore:微信-打款-100000111:1657164225
key: success value: 10 (次數(shù))
key: fail value: 5
key: balance_not_enough value: 3
key: thrid_error value: 2
.......
5.2 核心算法
為了避免兩次監(jiān)控間的小高峰被忽略,確保不漏報(bào),我們的算法采用的是局部 計(jì)數(shù)法 加上整體 滑動(dòng)窗口 的方式來實(shí)現(xiàn)的,每秒一個(gè)計(jì)數(shù)的點(diǎn)位,記錄成功和失敗的數(shù)量,監(jiān)控時(shí)會(huì)計(jì)算整個(gè)窗口時(shí)間范圍內(nèi)的成功失敗數(shù),最終得出每個(gè)通道的失敗率,比如:窗口時(shí)間是1分鐘,監(jiān)控頻率 10秒/次。
核心算法
那么監(jiān)控頻率是多少、時(shí)間窗口范圍如何設(shè)定,這都會(huì)影響我們最終監(jiān)控的準(zhǔn)確性。如果監(jiān)控頻率過小,就會(huì)導(dǎo)致我們的取數(shù)樣本太少,結(jié)果也沒有參考意義。如果監(jiān)控頻率過大,兩次窗口之間的小高峰就可能存在漏報(bào)的情況,這些都是影響告警準(zhǔn)確性的因素,這就需要通過對(duì)各個(gè)通道單據(jù)量級(jí)如:每天、每小時(shí)單量、下單頻率等指標(biāo)進(jìn)行分析而確定。
5.3 小流量的處理
小流量的通道和時(shí)間段要如何處理
- 從通道維度看,小流量的通道如何處理
- 從時(shí)間維度看,底峰時(shí)間段如何處理
比如轉(zhuǎn)轉(zhuǎn)接入的某一通道,每天總量只有幾單,或者凌晨的單量就是很少,針對(duì)這種小流量的處理方式是,在監(jiān)控時(shí)間窗口內(nèi)只有 1 單且失敗,則會(huì)擴(kuò)大時(shí)間窗口,比如我們正常時(shí)間窗口是 1 分鐘,那么擴(kuò)大 1 倍后,時(shí)間窗口則變更為 2 分鐘,1-10 倍逐級(jí)增加,擴(kuò)大到 10 倍之后如果還是高于預(yù)警閥值則會(huì)觸發(fā)告警,此時(shí)我們認(rèn)為這種也是需要關(guān)注的異常。
6 最終效果
- 通道異常告警,快速定位問題
通道異常告警
- 合并重復(fù)告警項(xiàng)
合并重復(fù)告警項(xiàng)
- 通道異常恢復(fù)
通道異常恢復(fù)
7 未來規(guī)劃
目前的支付通道自動(dòng)化管理系統(tǒng)還需要在以下幾個(gè)方面進(jìn)行優(yōu)化和升級(jí):
- 持續(xù)優(yōu)化監(jiān)控算法,提升告警準(zhǔn)確率到99%以上;
- 與監(jiān)控系統(tǒng)配合,實(shí)現(xiàn)通道故障時(shí)自動(dòng)下線的能力;
- 與監(jiān)控系統(tǒng)配合,實(shí)現(xiàn)故障恢復(fù)探測及通道自動(dòng)上線的能力;
關(guān)于作者:張丹,轉(zhuǎn)轉(zhuǎn)支付結(jié)算技術(shù)部研發(fā)工程師,主要負(fù)責(zé)清結(jié)算方向的研發(fā)工作