一、引子
根據CAP原理,分散式系統無法在保證了可用性(Availability)和分割槽容忍性(Partition)之後,繼續保證一致性(Consistency)。我們認為,只要存在網路呼叫,就會存在呼叫失敗的可能,系統之間必然存在著長或短的不一致狀態。在服務化流行的今天,怎樣及時發現系統服務間的不一致狀態,以及怎樣去量化衡量一個系統的資料一致性,成為每個分散式環境下的開發者需要考慮並解決的問題。
二、背景
以交易鏈路為例,存在著如下一些潛在的不一致場景:
- 訂單支付成功了,但是訂單狀態卻還是“待付款”
- 物流已經發貨了,但是訂單上面還是“待發貨”
- 銀行退款已經到賬了,但是訂單上面還是“退款中”
- 訂單發貨已經超過7天了,但是卻沒有自動完成
- …
上述每個業務場景,都可能產生使用者反饋,給使用者帶來困擾。業務對賬平臺的核心目的,就是及時發現類似問題,並及時修復。使問題在反饋前即被提前處理。
三、挑戰
那麼一個業務對賬平臺,會面臨著哪些挑戰?
我們對於一個業務對賬平臺的核心訴求,主要包括要方便業務系統快速接入,要能處理業務方海量的資料,並保證一定的實時性。這會深刻影響業務對賬平臺的系統設計。
四、架構
從區域性到整體,本文先從解決上面三個問題的角度,來看有贊業務對賬平臺的區域性設計,再來看整體系統結構。
4.1 易於接入
我們認為所有的對賬流程,都可以分解為“資料載入”、“轉換解析”、“對比”、“結果處理”這 4 步。為了適應多樣化的業務場景,其中的每一步都需要做到可編排,放置各種差異化的執行元件。在每一個流程節點,需要通過規則可以自由選擇嵌入哪個元件。其次,需要把資料從原始格式,轉換到對賬的標準格式(基於標準格式,就能做標準的通用對比器)。總結起來,我們認為對賬引擎需要具備以下的能力:
- 流程編排能力
- 規則能力
- 外掛化接入能力
目前業務對賬平臺的對賬引擎結構如下:
其中的 ResourceLoader 、 Parser 、 Checker 、 ResultHandler 均為標準介面,所有實現了對應介面的 spring bean ,都能被編排到對賬流程之中,包括業務方自己實現的 plugin。這樣就實現了外掛化和可編排。每個流程節點的功能如下:
- ResourceLoader :基於各種資料來源(DB、FILE、RPC、REST等)提供載入器工廠,載入各個資料來源的原始資料。載入的方式支援驅動載入、並行載入、多方載入等方式。業務方也可以自己實現載入器,利用流程編排能力嵌入到對賬流程中。
- Parser :對已載入的原始資料進行建模,轉換為對賬標準模型。利用規則引擎,提供指令碼化(Groovy)的轉換方式。
- Checker :按照配置對指定欄位、按指定規則進行比較,併產生對賬結果。支援 findFirst(找出第一個不一致)、full(找出所有不一致)等對比策略。
- ResultHandler :使用指定的handler對結果進行處理,常見的處理器包括持久化、傳送報警郵件、甚至直接修復資料等等。
通過統一的 facade,將整個對賬流程進行串聯。在執行不同節點時,根據配置選擇不同的預設元件或者外掛來執行。可以在管理後臺,對每個流程節點進行編排:
4.2 高吞吐量
一些離線定時對賬場景,單次對賬的資料量可能達到百萬級,甚至千萬級。這對對賬平臺的吞吐量造成了挑戰。我們面對海量資料問題的通常解決思路,就是“拆”。分散式任務拆分+單機內任務拆分,將資料塊變小。同時,也可以利用一些大資料的工具來幫我們減輕負擔。
目前的對賬有 2 種模式:一種常規模式,是通過資料平臺(包含了所有要進行對賬的原始主鍵資料,如訂單號)將資料 push 到對賬中心的 DB ,然後訂單中心叢集通過分片策略,並按分頁分批載入,載入資料進行對比。另一種,則是當資料量超過千萬時,利用資料平臺的 spark 引擎從 hive 表中獲取資料,然後投遞到 nsq(自研訊息佇列)。nsq 會選擇其中一個 consumer 進行投遞(不會投遞到多個consumer)。這樣千萬級的資料會變成訊息被分散的對賬伺服器執行。
對賬任務一般會選擇在業務量較小的凌晨進行,是因為在對賬過程中會需要通過反查業務介面,來獲取實時資料。而更好的情況是,對賬時能去除對業務介面的反查。因此,會需要對業務資料進行準實時同步,提前進入對賬中心的 DB 叢集。
主要思路是基於業務 DB 的 binlog 日誌或者業務系統自身的訊息,進行資料同步。後續流程則類似。
4.3 高實時性
一些特定的業務場景,比如買家已經付款成功了,但是由於銀行第三方的支付狀態回撥延遲,導致訂單狀態還是待付款。這種情況,買家會比較焦急,可能產生投訴。面對這樣一些場景,就需要進行實時對賬,也可以叫做秒級對賬。
秒級對賬往往基於業務訊息進行觸發,需要在事件觸發後的短時間內執行完對賬任務。且事件訊息的觸發,往往具有高併發的特點,因此需要相應的架構來進行支援。
設計中主要加入了 EventPool 來緩衝處理高併發的事件訊息,並加入限流、取樣、路由、處理的 pipeline。同時在進入事件處理執行緒池之前,需要進入阻塞佇列,避免大量的請求直接耗盡執行緒資源,同時實現事件處理的非同步化。處理執行緒批量定時從阻塞佇列獲取任務來執行。同時,利用延遲阻塞佇列,還可以實現延遲對賬的特性。(我們認為出現不一致的情況時,如果立即去進行對比,往往還是不一致的。所以就需要根據情況,在事件發生後的一段時間內,再觸發對比)
4.4 整體設計
上面介紹了業務對賬平臺的各個區域性設計,下面來看下整體結構。
整體上主要採用排程層+對賬引擎(core+plugin)+基礎設施的分層架構。排程層主要負責任務觸發、任務拆分、排程;對賬引擎則負責執行可編排的對賬流程;基礎設定層則提供規則引擎、流程引擎、泛化呼叫、監控等基礎能力。
五、健康度
對賬中心可以拿到業務系統及其所在整個鏈路的資料一致性資訊。基於此,對賬平臺具備了給予業務系統和鏈路健康度反饋的能力。
六、共建
前面有提到,對賬的流程被拆分為四個固定的流程節點,且有四個對應的標準介面。通過流程引擎和規則引擎的能力,可以根據 spring bean name,來將系統預設元件或者外掛編排到對賬流程之中。基於這種開放性的設計,業務對賬平臺支援與業務團隊進行共建。
首先,對賬平臺提供標準介面的 API jar 包,業務方通過引入 jar,實現相關介面,並將 impl 打包。這樣,對賬平臺通過 spi 的方式,可以引入業務方的外掛包,並載入到對賬中心的 JVM 中執行。
七、未來
業務對賬平臺,是面向業務場景建立,但同時屬於資料密集型應用。平臺上線以來,已經接入公司各團隊數十個對賬任務,每天處理千萬級的資料。展望未來,業務對賬平臺的使命會從主要進行離線資料分析處理,演進到利用應用系統的健康度資料幫助系統進行實時調整的方向。在分散式環境下,沒有人能迴避資料一致性問題,我們對此充滿著敬畏。歡迎聯絡 zhangchaoyue@youzan.com,交流心得。