作者|伍振河
行業背景
基金公司的核心業務主要分為兩部分,一部分是投研線業務,即投資管理和行業研究業務,它展現了基金公司核心競争力。另一部分是市場線業務,即基金公司利用自身管道和市場能力完成基金銷售并做好客戶服務。
博時基金管作為中國内地首批成立的五家基金管理公司之一,截至 2021 年 6 月 30 日,博時基金公司共管理 276 隻公募基金,管理資産總規模逾 15482 億元人民币,累計分紅逾 1465 億元人民币。

随着網際網路技術發展,基金銷售管道更加多元化,線上成為基金銷售重要管道。相比傳統基金客戶,線上管道具有客戶基數大,水準參差不齊的特點。對于那些還不成熟的客戶,我們需要做好陪伴,讓他們了解風險,了解投資。
RocketMQ 在陪伴體系中的應用
1、陪伴場景概述
博時基金建立了一套全方位多層次陪伴體系,從使用者層面、市場層面和産品層面為使用者提供投前、投中、投後的有溫度的投資陪伴體驗。
每個陪伴場景的達成,需要公司多個部門不同團隊協同配合來完成。依賴與投研、合規、營運、大資料等上下遊多個系統。但這些系統可能采用不同技術架構,實作方式各異,如果采用同步調用方式來實作協同,耦合度太高,不利于未來擴充。
2、RocketMQ 解耦異構系統
RocketMQ 提供高效可靠的消息傳遞特性和釋出訂閱機制,非常适合用于這種上下遊異構系統間的解耦。我們把原來基于檔案、郵件的協作方式全部線上化、流程化和機制化,大大提升了陪伴輸出效率。對于這種涉及多方系統的協作,需要對消息進行合理地歸類,以便進行過濾和索引。RocketMQ 提供的 Topic 和 Tags 就是用來做這件事的。
3、Topic 和 Tags 最佳實踐
Topic 與 Tag 作為業務上用來歸類的辨別,分别屬于一級分類和二級分類,這種階層化的分類辨別與企業組織架構比較類似,可以結合起來實作消息過濾。舉個例子,對于陪伴系統的 Topic,營運系統訂閱營運類消息,我們給這類消息打上 TagA 的标簽,客服系統訂閱客服類消息 TagB,陪伴編排系統訂閱編排類消息 TagC,合規系統需要對營運和陪伴消息進行合規審查,是以它需要訂閱 TagA 和 TagC,最後是資料中心,所有的消息都要處理,是以它需要監聽所有 Tag。
RocketMQ 事務消息的金融應用場景
1、金融場景概述
接下來,我們講解一下典型的金融場景--優惠購。在博時基金 APP 上申購基金可以享受低至 0 折的費率優惠,具體業務怎麼樣實作?這裡有有兩種方式,第一種先充值博時錢包,底層是替客戶購買了一筆貨币基金,然後再用博時錢包購買目标基金。這種方式需要使用者操作兩次,比較繁瑣,容易引起客單流失。另外一種方式就是優惠購,把兩步購買基金封裝成一次事務操作。對投資者來說,開啟優惠購服務後,操作少一步,投資更簡單!
2、領域事件理論模型
領域事件是指業務流程的一個步驟将導緻進一步的業務操作,比方說登入事件,比方說基金購買事件等。在領域模型裡面,領域事件事務采用的是最終一緻性,差別于強一緻性,它是弱一緻性的一種。在領域模型映射到微服務系統架構時,微服務之間的資料不必要求強一緻,是以領域事件可以解耦微服務。依據是否跨微服務,可以分為兩種場景:
第一種場景:當領域事件發生在同一個微服務。由于大部分事件發生在同一個程序内,自身可以很好地控制事務。但如果一個事件需要同時更新多個聚合,按照 DDD 中一次事務隻更新一個聚合的原則,就需要引入事件總線,就是 eventbus 這種模式。
第二種場景:跨微服務。領域事件發生在微服務之間的場景比較多,事件處理的機制也更加複雜。跨微服務的事件可以推動業務流程或者資料在不同的子域或微服務間直接流轉,是以需要一個協調者來推進全局事務。跨微服務的事件機制要總體考慮事件建構、釋出和訂閱、事件資料持久化、消息中間件、分布式事務機制等,其中具備事務消息功能的消息中間件是這個解決方案的核心元件。
3、分布式事務方案對比
在博時基金的業務場景下,需要解決的問題是事務一緻性與服務解耦度之間的沖突,是以我們的目标是讓主從事務解耦,保證核心邏輯穩定,同時不因為解耦而犧牲最終一緻性。是以,當時做出了幾種不同的解決方案:
- 第一種方案:最常見普通消息+異步對賬,這個方案的問題是無法保證主事務的執行和入隊同時成功,需要時效性低的對賬補償解決,一緻性隻是較高。
- 第二種方案:本地消息表,對比上一種做法,它由業務将寫入消息表放到主事務中,把主事務和入隊變成一個原子操作,然後業務讀取入隊記錄,自己投遞給從事務。它的缺點是主事務和消息表在存儲上是耦合的,沒有解耦度。
- 第三種方案:引入 XA 事務,是個兩階段送出的協定,實作難度較大。而且面臨兩個問題:一是這是一種同步阻塞協定,有鎖占用導緻并發不會太高,另外就是 XA 事務過程中,在參與者投贊成票後,如果協調者發生故障,節點不清楚應該送出還是中止,隻能等待協調者恢複。這時候可能會出現業務中斷。
- 第四種方案:TCC,專門處理分布式事務的 TCC,隻側重于一緻性,無解耦度,也是不可行。
- 第五種方案:事務消息,它能同時兼顧解耦度和一緻性,是最合适的模式。
最終我們選擇了 RocketMQ 的事務消息作為分布式事務的解決方案。
4、RocketMQ 事務消息核心流程
基于 RocketMQ 的事務消息搭建事務中心,協調分布式事務的推進和復原。以優惠購為例,核心流程如下:
- 第一階段:Prepare 階段 ,即業務系統将 RocketMQ 的半事務消息發送到事務中心,事務中心不做釋出,等待二次确認。這個階段 RocketMQ 的半消息在消費者端是感覺不到的。
- 第二階段:業務系統執行主事務,即購買貨币基金。
- 第三階段:主事務成功後 commit 到事務中心,由事務中心投遞消息到從事務。如果主事務失敗,就投遞 rollback 給事務中心。這裡需要兩階段送出的原因是:普通的入隊操作無論放在主事務之前還是之後都無法保證最終一緻。如果先執行主事務,再入隊,那麼可能在入隊前,業務會當機,就沒有機會再入隊了。如果先入隊再執行主事務,那麼可能主事務沒有執行成功,但是從事務執行成功了,業務邏輯就會發生錯亂。
由于網絡抖動等原因,可能導緻事務消息的二次确認丢失。此時需要依賴某種機制恢複整個分布式事務的上下文,RocketMQ 提供的反查機制正是為解決分布式事務中的逾時問題而設計的。我們的事務中心的反查機制流程主要是,先檢查事務中心的内部狀态,再通過反查接口檢查本地事務的執行結果,恢複事務上下文後,正常推進後續的流程。
5、RocketMQ 如何保證事務消息在消費端正常消費
消費端消費失敗後,MQ 服務端需要進行一定次數的重試,我們需要制定合理的重試政策。因為有消費重試,這要求消費方接口需要實作幂等性;如果重試多次後仍失敗,我們會把消息壓入死信隊列 DLQ,RocketMQ 提供了死信隊列的功能,對進入死信隊列的消息進行告警處理。
6、事務消息的适用場景
第一類場景:需要同步執行的領域事件,比如說領域事件邏輯失敗機率大,業務要及時将傳回碼告知用戶端,自然不能放在異步流程中。舉個例子,做過支付系統的小夥伴都知道,支付扣款前要檢查餘額是否足夠,如果餘額不足,那在異步流程中重試多少次都是失敗。
第二類場景:是事務不可重入場景,例如業務系統發送消息時沒有确定一個唯一事務 ID,那後續的業務邏輯就無法保證幂等,假設其中一個事務是建立訂單,如果不能保證幂等的話,重試多次就會産生多個訂單;是以這裡需要使用到事務消息,用來明确一個分布式事務的開始,生成一個唯一事務 ID,讓後續的流程能以這個事務 ID 來保證幂等。
未來規劃
目前,我們基于 RocketMQ 在客戶陪伴體系上解耦了上下遊的服務,提升了營運和陪伴的效率。同時,我們在 RocketMQ 事務消息的基礎上,搭建了這樣一個支援分布式事務的服務協調平台,也就是我們的事務中心,大大提升了對金融場景化的産品包裝能力。未來,我們将圍繞着事務中心,拓寬更多的金融應用場景,創造更大的業務價值。