金融行業訊息佇列選型及實踐
文章深度解讀了某商業銀行做訊息佇列選型時考慮的因素,包括關鍵需求、選型要點、選型原則等,同時給出了選型建議、產品對比以及典型場景和二次封裝的建議。本文作者在自己豐富實踐經驗基礎上抽象出一些方法論,供讀者在做訊息佇列技術選型時參考。
本文主要內容包括以下方面:
1 概述
2 為什麼引入訊息佇列
何為訊息佇列
訊息佇列的優勢
訊息佇列的不足
3 訊息佇列選型
關鍵需求
其他需要考慮的因素
選型要點及原則
選型建議
候選產品比較
選擇RocketMQ的原因
4 二次封裝建議
5 典型場景
支付場景
第三方延時呼叫場景
6 經驗總結和建議
作為金融企業對公眾提供服務一定要保證其可用性,儘量做到更多個9(一般SLA中描述的高可用性99.99%,中的9越多代表全年服務可用時間越長服務更可靠,停機時間越短),隨著軟體系統的複雜度越來越高,故障是不可避免的。這就需要企業實現整體的彈性架構(Resilient Architecture),為應對故障而設計。
然而,常見的RPC、RMI企業整合技術,因為是同步請求,常因為執行方失敗、超時等因素而影響終端使用者體驗,而且很多故障是無法徹底消除的。而且RPC和RMI呼叫需要服務的消費方和服務的提供方同時線上,並且雙方需要通過某種機制確認彼此的呼叫關係,因為存在這些弊端,就導致了面向訊息的中介軟體(MOM)的產生,通過在企業架構中引入訊息中介軟體,確保在故障發生時,受此影響的系統部分在一個很小的範圍內。
訊息中介軟體就是在分散式系統中間引入一個透明的中間層,隔離服務的提供方和消費方。
2.1 何為訊息佇列
訊息佇列(Message Queue,MQ)是一種不同應用程式之間(跨程式)的通訊方法,用於上下游應用程式之間傳遞訊息。我們拆分來看:
訊息:應用程式通過寫入和檢索出入列隊的資料(訊息)來通訊。
佇列:除去了接收和傳送應用程式同時執行的要求。
這樣就實現了上游與下游之間的解耦,上游向MQ傳送訊息,下游從MQ接收訊息,上游下游互不依賴,它們只依賴MQ。因為有佇列的存在,MQ可在上下游之間進行緩衝,把上游資訊先快取起來,下游根據自己的能力從MQ中拉取資訊,起到削峰的作用。
2.2 訊息佇列的優勢
1 解耦
1)什麼是耦合
高內聚低耦合,是軟體工程中的概念,這裡的低耦合是指各個元件之間,儘可能相互獨立。通俗一點的理解就是,增加模組間呼叫透明化,最高的透明度就是不用知道彼此的存在,因此減少介面的複雜性、規範呼叫的方式及傳遞的資訊,降低產品各模組的依賴,提高重用程度。
2)如何解耦
在企業整體架構中解耦,主要設計兩個方面:一是簡化減少互動,二是增加一箇中間層實現兩方的隔離,MQ就是其中的中間層(如下圖所示)。引入MQ後生產者和消費者不必知道彼此的存在也不必同時線上,主要互動流程如下:
生產者負責:生產訊息並通過SDK或API呼叫傳送給MQ(同步傳送或者非同步傳送);
MQ負責:接收訊息,並持久化訊息到訊息儲存(同步或非同步儲存訊息);
生產者接收來自MQ的響應(訊息傳送狀態或異常);
消費者訂閱訊息後,接收來自MQ的訊息;
消費者執行訊息對應的後續業務操作;
對消費結果進行確認(消費成功、失敗、異常等)。
削峰填谷
2 削峰填谷
由於系統閒忙分佈不均,QPS常相差幾十倍甚至更高,特別是在遇到營銷活動時,瞬間流量很可能超過後端系統的承載能力,這就要考慮通過訊息中介軟體來緩衝,MQ客戶端例項根據自己的處理能力從MQ伺服器拉取訊息,以此來減輕或消除後端系統的瓶頸。
(圖片來源:https://github.com/alibaba/Sentinel/wiki/Sentinel-為-RocketMQ-保駕護航)
3 異構整合
由於各種原因,我們在企業資訊化建設過程中,都會面臨軟體產品來自不同的廠家只解決某特定領域的問題,這些產品因為封閉的架構無法對外提供服務或缺少核心開發而無法做大的改造,這就造成了彼此之間很難整合。通過引入MQ可以部分解決該問題,只需要在某個環節生產一條訊息,或者根據訊息做出具體的響應,只需與MQ對接,不必與其他系統做一對一的對接。
4 非同步隔離
為了提供金融服務的整體彈性,需要隔離內部、外部系統間的依賴。如支付通知分為兩種,一種是同步通知,這時API呼叫會因為網路故障而超時,因為服務提供方處理能力限制而得不到及時響應等多種因素影響,另一種是非同步通知,在一定時效範圍內最終通知到即可,從而提供提高終端使用者的體驗和交易成功率,提高業務的整體生產率。
2.3 訊息佇列的不足
凡有收益必有代價,MQ也有其不足:
在支援靈活性的同時,增加了系統的整體複雜度。
因為非同步呼叫的延時大於RPC同步呼叫是,所以會出現短暫的不一致性
無法做到事務的強一致,需要分散式事務方案來處理
服務的消費方要做冪等設計,來規避重複呼叫的問題
所以在軟體的正常功能開發中,並不需要去刻意的尋找訊息佇列的使用場景,而是當出現效能瓶頸時,去檢視業務邏輯是否存在可以非同步處理的耗時操作,如果存在的話便可以引入訊息佇列來解決。否則盲目的使用訊息佇列可能會增加維護和開發的成本卻無法得到可觀的效能提升,那就得不償失了。
但凡選擇就會受到主觀和客觀兩個因素的影響。我們如何儘量客觀的進行架構和框架選型,而避免先有結果而後找理由的文字遊戲,下面我分享下我們做MQ選型的過程(這裡不是說主觀就是不好的,但作為工程師凡事做結構化和量化還是有必要的)。
3.1 關鍵需求
叢集支援:為了保證訊息中介軟體的可靠性,需要提供完備的生產者、消費者、訊息中介軟體叢集方案;
持久化的支援:為了避免訊息丟失,需要支援訊息儲存到磁碟檔案或其它格式儲存;
訊息重試的支援:訊息處理失敗後的支援失敗轉存或重試,並提供訊息至少投第一次或訊息最多投遞一次的配置;
分散式事務的支援:為了保證業務的完整性,選擇的中介軟體需要支援分散式;
訊息的按序消費:在有些場景下,需要訊息的消費能夠按照傳送的同樣順序進行處理從而保證順序執行;
訊息的延時支援:在2C業務處理或三方資料來源對接中,會遇到訊息延時投遞要求,需要支援延投遞;
訊息堆積和回溯功能:在訊息中介軟體持久化儲存大量訊息時不會對效能有大的影響,支援訊息查詢、重發,或者按照時間點來重新消費訊息,以應對某一段時間訊息的重新消費場景。
3.2 其他需要考慮的因素
產品與當前技術棧是否匹配,團隊人員熟悉原始碼更便於對訊息中介軟體的原理理解和後續功能擴充套件;
產品的使用廣度特別是金融同業客戶:同業因為業務同質化校對,場景需求相近,使用的人越多,說明關鍵場景支援較好,問題在之前暴露的越充分,當我們在使用時碰問題的時候,就比較容易找到對應的解決方案或解決人員;
產品的高可用性:作為一個金融企業,需要服務的持續可用,作為提高企業彈性的基礎訊息平臺,叢集和高可用是一個必不可少的要求;
產品的穩定性:產品可以持續、穩定的提供服務,不需要經常因為資源洩露或效能衰減等問題而重新啟動。
產品的活躍度:通過github統計資料能看出來這個產品是否經常有人維護,經常有人開發一些新的功能,經常fix一些bug。
3.3 選型要點及原則
搜尋滿足關鍵需求的框架到候選清單;
從功能和非功能性需求等幾個方面對候選框架進行篩選;
在選型過程中要做好量化記錄,避免先有傾向性的結果,後有篩選,這樣選型就變成了一場數字遊戲;
有時要換個角度思考,常用來做比較的可能就是最好的,如很多MQ框架都與Kafka做比較,那麼Kafka有可能就是最通用的框架,如果做選型就要對其是否滿足自己的需求做重點分析;
遵循第三眼美女原則,讓理性引導感性;
適合的就是最好的,不要但純追求高效能和功能全面。
3.4 選型建議
為未來最少三年或五到十年來選擇,因為TedNeward在JAVA 訊息服務的序言中總結了技術熟悉的過程4個階段(門外漢、探索者、熟手、大師)。選型到全範圍推廣結束一年左右的時間就過去了,到大家熟悉和精通又一年過去了,誰都不想在剛熟悉還沒用好,當前的產品就不滿足要求了,又要重新來過。
區分關注點,確保只針對核心關注點進行選擇。給出選擇的deadline,並按時進入到專案實戰準備,再多的理論分析,都不如真正使用過後的感受深入,產品需要成長、團隊成員同樣需要成長,既然路在遠方就趕緊起程。
3.5 候選產品比較
1 產品特性比較
2 測試建議
功能測試:建議搭建POC環境進行驗證,除驗證相關功能性指標有沒有,還要驗證好不好用。所以在測試時要基於MQ提供的功能構建使用場景進行業務功能實現的驗證。
效能測試:其實效能測試涉及的各方面因素比較多,如:基於什麼樣的環境,做了哪些配置,採用什麼樣的壓測指令碼和報文來做壓力測試?比較指標:除TPS(傳送者TPS、消費者最終處理業務的TPS)、延時、支援多少同時線上連結(生產者資料量、消費者資料量)、Topic配置(Topic數量以及每個Topic佇列數量與生產者、消費者資料量的關係)、伺服器的效能指標(cpu、記憶體、磁碟io、網路io)如何等也是需要考量的。
疲勞測試:在一定壓力下持續執行24小時、一週或更長時間。要重點關注穩定性、伺服器的各項指標、是否有緩慢增長的趨勢等。
重啟或故障演練:分別對註冊中心NameServer、Broker、Producer、Consumer的例項進行部分重啟(或直接kill)或全部重啟(或直接kill)、磁碟故障、網路故障等,檢視應用的影響,如:在RocketMQ服務是否可以恢復,生產者消費者是否可以恢復服務,訊息是否有丟失,訊息是否有重複等。
3.6 選擇RocketMQ的原因
ActiveMQ的優勢在於支援協議多樣、文件資料豐富,缺點是效能、順序投遞支援有限;
Kafka的優勢在於高吞吐率,缺點是分散式事務、消費失敗重試、延時/定時訊息支援有限;
RabbitMQ的優勢在於與SpringBoot整合好,缺點是分散式事務、延時/定時訊息支援有限;
RockeMQ的優勢在於高吞吐率、順序訊息、延時訊息、訊息堆積、訊息回溯等支援較好,缺點是支援協議有限,多語言客戶端支援有限。
我們最終選擇RocketMQ的主要原因如下:
金融服務有場景對順序訊息有嚴格要求;
金融服務有場景對延時訊息需求;
為了保證最終一致性需要支援分散式事務;
為了保證一致性訊息對賬需要MQ中介軟體支援訊息查詢;
RocketMQ在高一致性(持久化、訊息重試)做的比較好;
行內使用的JAVA技術棧,暫不需要多協議和多語言支援;
RocketMQ的使用者有國內知名的網際網路金融公司、有微眾銀行、民生銀行這樣的互聯銀行和直銷銀行代表、有企業軟體服務商,從3.1選型需求匹配和主要使用者可以看出RocketMQ對金融場景適配比較好行業認可度高,隨著金融使用者的使用未來更多的需求將會被滿足。
封裝主要是對業務、技術和資料進行抽象和封裝,封裝具有如下優點:
透明,通過引入二次封裝SDK膠水層,隱藏具體實現細節
重用,提高基礎程式碼的重用性同時增加的程式碼的可維護性和可靠性
安全,通過規範操作提高安全性
將規範封裝到基礎程式碼中以便在企業內部統一互動標準,具體的規範包括:
topic命名規範
producter命名規範
Consumer命名規範
基於MessageId、key或業務ID的冪等通用方案封裝
通過賦義編碼規範可以通過名稱定位到所屬專案、模組等業務場景,以便在出現未知問題時可以快速協調人員進行處理,當然對topic、生產者、消費者等原資料管理也是必要的,畢竟命名規範能保留的資訊有限。通過命名規範還可以避免衝突,比如topic的衝突會或誤會導致訊息無法正常消費或者業務流轉等問題,消費者GroupID衝突會導致訊息丟失等問題。
通過封裝和定製化增強管理功能如批量資訊查詢、批量資訊重發、訊息對賬等。
RocketMQ是提高整體服務彈性的重要基礎中介軟體,在個類金融交易中承擔著重要的角色。
4.1 支付場景
下面以活期賬戶轉出服務場景為例來講解。
出於安全考慮我們每筆使用者活期賬戶轉出都要傳送一條簡訊通知,這時通過RPC呼叫即可實現,如下圖所示:
隨著業務發展及出於安全和反欺詐考慮,建設了反欺詐服務,需要在每筆轉出時傳送支出場景的埋點資料,這個時候有兩種方案,一種是繼續使用RPC呼叫,如下圖所示,這時轉出操作響應時間可能是處理耗時A+簡訊呼叫時間B+反欺詐服務呼叫時間C(也有可能是通過非同步RPC呼叫:響應時間轉出操作時間+Max(B,C)),除了耗時之外,系統的故障點也多了,如果反欺詐服務或者簡訊服務當機了,那麼很大可能活期賬戶轉出服務會隨之宕掉(如果使用了熔斷和隔離,可以很大程度規避跨系統間的呼叫異常影響)。
第二種解決方案是未雨綢繆,既然現在有第二個服務需要了解事件,那麼以後是不是還會有第三個、第N個呢?如下圖所示。這時活期賬戶轉出服務的穩定性會進一步降低,複雜度急劇上升,在處理登入邏輯的同時需要考慮N個系統的關聯需求。
如果從領域模型上看這些是活期賬戶轉出服務應該做的嗎?顯然不是,轉出應該只關注與本身的賬戶操作,其它服務應該依賴轉出的事件來做後續的相關業務邏輯,在這樣的場景中適合用訊息中介軟體來解耦。如下圖所示。
這樣各服務迴歸到本來應該的樣子,賬戶轉出服務不用關注於其它系統對它的依賴,只需要產生賬戶轉出事件的訊息,有需要的服務各自去訂閱這個訊息就好了,相互不受影響。
4.2 第三方延時呼叫場景
在與銀行外部系統對接的時候,會有需要在傳送服務請求N秒後,去獲得響應結果的非同步請求。在使用RocketMQ的延時訊息前,都是通過將資料落地到資料庫或Redis快取中,然後通過定時輪詢任務來操作。這樣做有以下缺點:
非通用方案,每個系統都需要單獨維護一個輪詢任務;
資料量大了以後輪詢會影響資料庫效能;
輪詢時間不宜設定過低,常以分鐘為單位輪詢,時效性差,如果時間過短對資料庫壓力也會增加;
針對輪詢失敗的重試機制需要各自實現。
使用RocketMQ後有以下優點:
通用延時方案,生產延時訊息和訊息消費隔離;
支援高併發,具備較好的橫向擴充套件能力;
時效性大幅提升,可以精確到秒級。
不過,當前版本還不支設定時間精度,只能按照特定的messageDelayLevel來設定,這就要在搭建RocketMQ時提前最好延時級別的規劃或者對RocketMQ延時原始碼進行擴充套件以支援指定時間精度。
Topic佇列數設定不合理導致負載不均衡,影響RocketMQ的Broker的穩定。
訊息堆積導致投遞延時時間變長。
因為消費者的叢集ID設定不規範,導致A消費者消費了B的訊息。
因為Topic設定的不合理導致一個Broker節點的當機導致訊息消投遞和消費異常。
Topic、消費者GroupID相關命名不規範,導致執行一段時間後RocketMQ的Topic混亂無法清理回收資源。
當故障是不可避免的,就需要在應用程式設計的時候考慮通過一系列機制具備自我管理、自我恢復、自我配置等自治功能,隨著雲原生架構的流行,面對負載的加重和不斷髮生的各類故障,MQ不會是彈性系統設計的唯一選擇,但也許是一個不錯的選擇,值得一試。
作者簡介:金融業老兵,十餘年金融行業工作經驗,愛思考和分享。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31562044/viewspace-2636363/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 阿里雲訊息佇列 Kafka-訊息檢索實踐阿里佇列Kafka
- 深度 | 金融級訊息佇列的演進 — 螞蟻金服的實踐之路佇列
- 訊息佇列中介軟體的選型與比較佇列
- 訊息佇列系列一:訊息佇列應用佇列
- Redis實現訊息佇列Redis佇列
- 2-如何選擇訊息佇列佇列
- 訊息佇列佇列
- 雲訊息佇列 ApsaraMQ 成本治理實踐(文末附好禮)佇列MQ
- 金融業分散式資料庫選型及HTAP場景實踐分散式資料庫
- RabbitMQ 訊息佇列之佇列模型MQ佇列模型
- kafka 訊息佇列Kafka佇列
- 訊息佇列(MQ)佇列MQ
- [Redis]訊息佇列Redis佇列
- [訊息佇列]rocketMQ佇列MQ
- [訊息佇列]RabbitMQ佇列MQ
- Kafka訊息佇列Kafka佇列
- RabbitMQ訊息佇列MQ佇列
- 淺談訊息佇列及常見的訊息中介軟體佇列
- python多執行緒中訊息佇列如何實現?Python執行緒佇列
- 訊息佇列Rabbitmq的交換器型別佇列MQ型別
- 使用Spring Boot實現訊息佇列Spring Boot佇列
- 阿里二面:要保證訊息不丟失,又不重複,訊息佇列怎麼選型?阿里佇列
- 全面理解Handler-1:理解訊息佇列,手寫訊息佇列佇列
- rabbitmq訊息佇列原理MQ佇列
- 訊息佇列之 RocketMQ佇列MQ
- 訊息佇列二三事佇列
- MQ訊息佇列_RabbitMQMQ佇列
- 訊息佇列設計佇列
- 訊息佇列深入解析佇列
- 訊息佇列之 ActiveMQ佇列MQ
- 訊息佇列之RocketMQ佇列MQ
- 訊息佇列雜談佇列
- 訊息佇列之RabbitMQ佇列MQ
- 訊息佇列簡史佇列
- RabbitMQ訊息佇列(五):Routing 訊息路由MQ佇列路由
- RabbitMQ訊息佇列(六):使用主題進行訊息分發MQ佇列
- 如何實現MQ佇列訊息監控MQ佇列
- Redis 竟然能用 List 實現訊息佇列Redis佇列