張振宇
摘要:消息中間件是利用可靠高效的消息遞送機(jī)制幫助分布式系統(tǒng)進(jìn)行平臺數(shù)據(jù)交互的系統(tǒng)軟件。分析了消息中間件的Java消息服務(wù)(JMS)標(biāo)準(zhǔn)、消息模式和發(fā)展現(xiàn)狀,討論了消息中間件帶來的幾個優(yōu)勢和應(yīng)用消息中間件帶來的若干問題,并提出了解決方案。以SpringBoot工程中應(yīng)用ActiveMQ為例,通過編碼實(shí)現(xiàn)展示了應(yīng)用ActiveMQ的主要方法。
關(guān)鍵詞:JMS;消息中間件;ActiveMQ
中圖分類號:TP393.09文獻(xiàn)標(biāo)志碼:A文章編號:1008-1739(2022)14-43-5
云計算是分布式計算的一種,主要是通過將龐大的應(yīng)用服務(wù)拆分成多個較小的子程序,再分配給多臺服務(wù)器進(jìn)行處理,不同服務(wù)器的子程序之間必然需要通過網(wǎng)絡(luò)進(jìn)行通信協(xié)作,因而可靠、安全、高效的通信基礎(chǔ)設(shè)施對于云計算的成功非常重要。
通常,消息通信機(jī)制分為同步通信和異步通信2種。在同步通信中,客戶端直接請求服務(wù)端,并等待服務(wù)結(jié)果返回后才繼續(xù)執(zhí)行。這種實(shí)現(xiàn)方式結(jié)構(gòu)簡單,但是會造成客戶端阻塞,導(dǎo)致程序服務(wù)并發(fā)訪問效率低下,降低了服務(wù)的可用性。在異步通信中,客戶端和服務(wù)端不會直接進(jìn)行通信,而是客戶端把請求以消息的形式放入消息隊(duì)列中,服務(wù)端從消息隊(duì)列中獲取請求。這樣客戶端和服務(wù)端之間不會阻塞并等待對方響應(yīng),從而提高了并發(fā)訪問的效率。
異步消息通信機(jī)制使云計算各層次中內(nèi)部組件之間和各層次之間解耦合,同時有利于服務(wù)的動態(tài)伸縮性。異步通信機(jī)制經(jīng)過多年發(fā)展,逐步形成了以JMS標(biāo)準(zhǔn)規(guī)范為代表的消息中間件體系。
1.1 JMS
JMS是由SUN公司提出的基于Java平臺消息中間件的接口規(guī)范,定義了消息的發(fā)送和接收等操作接口、消息格式和各個模塊的功能語義[1]。JMS由生產(chǎn)者、消費(fèi)者、服務(wù)提供者和消息等部分組成。JMS是在Java標(biāo)準(zhǔn)化組織內(nèi)開發(fā)的標(biāo)準(zhǔn),2001年6月發(fā)布了JMS1.0.2b版本,定義了點(diǎn)對點(diǎn)和發(fā)布訂閱消息模式,當(dāng)前已不建議使用。2002年3月發(fā)布了JMS1.1版本,引入了統(tǒng)一的接口定義來實(shí)現(xiàn)2種類型的消息傳遞。2013年4月發(fā)布了JMS2.0版本,在1.1版本基礎(chǔ)上大大簡化了接口操作,使用戶能夠更加專注于業(yè)務(wù)邏輯的開發(fā)工作[2]。
1.2消息模式
在JMS中定義了2種消息模式:點(diǎn)對點(diǎn)模式和發(fā)布訂閱模式[3]。在點(diǎn)對點(diǎn)模式中,一條消息只能被一個接收者消費(fèi),是一對一的關(guān)系[4],點(diǎn)對點(diǎn)模式如圖1所示。
點(diǎn)對點(diǎn)模式有以下特點(diǎn):
①一條消息僅能被一個接收者消費(fèi)。
②消息并不是自動推送給接收者的,而是需要接收者主動從隊(duì)列中請求獲得。當(dāng)有多個接收者監(jiān)聽同一個隊(duì)列時,根據(jù)先請求先獲得的原則確定接收者。
③如果沒有接收者在監(jiān)聽該消息隊(duì)列,消息會保存在該隊(duì)列中,直到被消費(fèi)或超時。
④接收者收到消息后必須向隊(duì)列發(fā)送接收確認(rèn),否則消息中間件認(rèn)為該消息沒有被接收,仍可被其他接收者消費(fèi)。
⑤發(fā)送者和接收者運(yùn)行先后順序沒有限制。
在發(fā)布訂閱模式中,一條消息可以被多個訂閱者消費(fèi),是一對多的關(guān)系[4]。發(fā)布訂閱模式如圖2所示。
發(fā)布訂閱模式有以下特點(diǎn):
①消息中間件根據(jù)訂閱者所訂閱的主題將消息傳遞給訂閱者。
②訂閱同一個主題的多個訂閱者會接收到同一條消息。
③消息會自動廣播,訂閱者無需主動請求或輪詢主題獲得新消息。
④訂閱者必須先訂閱主題,然后再等待發(fā)布者發(fā)布消息。
1.3消息中間件發(fā)展現(xiàn)狀
目前主流消息中間件有ActiveMQ,RabbitMQ,ZeroMQ[5]。
ActiveMQ是一款由Apache組織發(fā)布的開源的(Apache License 2.0)獨(dú)立的企業(yè)級通用消息中間件,完整地實(shí)現(xiàn)了JMS1.1和J2EE1.4規(guī)范,同時支持集群部署、事物控制、存儲轉(zhuǎn)發(fā)和持久化等一些企業(yè)級特性。ActiveMQ可以借助JVM跨平臺的特性,可以運(yùn)行于現(xiàn)階段所有主流操作系統(tǒng)中。具備JMX和Web兩種控制臺,便于開發(fā)調(diào)試和運(yùn)行監(jiān)控,并且所有功能和特性都可以通過XML文件進(jìn)行配置[6]。
RabbitMQ是一個開源的高級消息隊(duì)列協(xié)議(Advanced Message Queuing Protocol,AMQP)實(shí)現(xiàn),服務(wù)器端用Erlang語言編寫,支持Python、.NET、Java,C語言、PHP等多種客戶端;支持消息持久化和崩潰恢復(fù),重新啟動應(yīng)用程序之后消息不會丟失;用于在分布式系統(tǒng)中存儲轉(zhuǎn)發(fā)消息,在易用性、擴(kuò)展性和高可用性等方面表現(xiàn)不俗。
ZeroMQ是一個開源的、跨平臺、高性能及精簡靈活的網(wǎng)絡(luò)消息中間件。支持新的輕量級socket風(fēng)格的接口,支持多種底層協(xié)議和AMQP,支持多種平臺和CPU架構(gòu)。具有獨(dú)特的非中間件的模式,不需要安裝和運(yùn)行一個消息服務(wù)器或中間件。只需要引用ZeroMQ程序庫,就可以在應(yīng)用程序之間發(fā)送消息,部署非常簡單。性能方面比其他消息中間件要強(qiáng)。但是ZeroMQ不支持消息持久化和崩潰恢復(fù),且穩(wěn)定性較差。
2.1異步處理
在分布式服務(wù)情況下,一次請求可能會調(diào)用多個子系統(tǒng)的接口,需要等待所有的接口都處理完畢返回響應(yīng),才能獲取最終的執(zhí)行結(jié)果。這種同步接口調(diào)用的方式總耗時比較長,非常影響用戶的體驗(yàn),特別是在網(wǎng)絡(luò)不穩(wěn)定的情況下,極易出現(xiàn)接口調(diào)用超時問題。
例如,用戶在注冊界面提交注冊信息后,系統(tǒng)需要處理注冊信息并寫入數(shù)據(jù)庫,用戶發(fā)送注冊郵件通知注冊成功,再向用戶發(fā)送注冊短信通知注冊成功,都處理完后返回用戶注冊界面提示用戶注冊完成。假設(shè)3個業(yè)務(wù)節(jié)點(diǎn)處理時間都是50 ms,不考慮網(wǎng)絡(luò)開銷等其他處理時延,用戶從提交注冊到顯示注冊成功的等待時間是150 ms,同步處理流程如圖3所示。
通過使用消息中間件將同步調(diào)用改為異步調(diào)用,將非必須的業(yè)務(wù)邏輯進(jìn)行異步處理改造,能夠顯著減少系統(tǒng)響應(yīng)時間。仍以用戶注冊為例,用戶在注冊界面提交注冊信息后,系統(tǒng)需要處理用戶注冊信息并寫入數(shù)據(jù)庫,再寫入消息隊(duì)列,然后直接返回用戶界面提示用戶注冊完成。向用戶發(fā)送注冊郵件通知和發(fā)送注冊短信通知都改為異步處理。假設(shè)3個業(yè)務(wù)節(jié)點(diǎn)處理時間都是50 ms,寫入消息隊(duì)列耗時5 ms,不考慮網(wǎng)絡(luò)開銷等其他處理時延,用戶注冊等待時間是55ms,異步處理流程如圖4所示。
2.2流量削峰
在類似秒殺的電商場景下,用戶請求瞬時激增,所有的請求最終都壓到數(shù)據(jù)庫,可能會導(dǎo)致數(shù)據(jù)庫響應(yīng)變慢甚至無響應(yīng),傳統(tǒng)模式流程如圖5所示。
使用消息中間件之后,用戶請求能夠緩存在消息隊(duì)列中,從而起到流量削峰的作用。訂單系統(tǒng)接收到用戶提交訂單信息后,將請求直接發(fā)送到消息中間件,然后其他業(yè)務(wù)處理從消息中間件中獲取訂單信息進(jìn)行處理和寫庫操作。由于消息隊(duì)列服務(wù)處理消息速度比數(shù)據(jù)庫快很多,如果出現(xiàn)請求峰值的情況,超量的消息能夠暫存在消息中間件的隊(duì)列中,訂單處理系統(tǒng)會按照自己的處理能力來消費(fèi)消息,不會對系統(tǒng)的穩(wěn)定性造成影響,流量削峰流程如圖6所示。
2.3系統(tǒng)解耦
復(fù)雜的業(yè)務(wù)系統(tǒng)一般都會拆分成多個子系統(tǒng)。以用戶下單為例,請求會先通過訂單系統(tǒng),然后分別調(diào)用支付系統(tǒng)、庫存系統(tǒng)、積分系統(tǒng)和物流系統(tǒng),系統(tǒng)之間耦合性太高。如果調(diào)用的任何一個子系統(tǒng)出現(xiàn)異常,整個請求都會異常,對系統(tǒng)的穩(wěn)定性非常不利。另外,如果需要增加商品推薦系統(tǒng),也需要對訂單系統(tǒng)進(jìn)行修改,業(yè)務(wù)系統(tǒng)耦合關(guān)系如圖7所示。
使用消息中間件相當(dāng)于在各子系統(tǒng)之間插入了一個基于數(shù)據(jù)的隱含接口層,各子系統(tǒng)都要實(shí)現(xiàn)這一接口,從而在保證接口不變的前提下,允許各業(yè)務(wù)系統(tǒng)分別進(jìn)行獨(dú)立的修改。新增商品推薦系統(tǒng)時,也不需要修改其他業(yè)務(wù)系統(tǒng),業(yè)務(wù)系統(tǒng)解耦后關(guān)系如圖8所示。
3.1可用性降低
引入消息中間件后,各業(yè)務(wù)系統(tǒng)都依賴消息隊(duì)列服務(wù),而一旦消息隊(duì)列服務(wù)失效,會導(dǎo)致各業(yè)務(wù)系統(tǒng)之間無法通信,所以需要考慮消息服務(wù)故障導(dǎo)致的可用性降低問題。
針對單點(diǎn)故障導(dǎo)致的可用性降低問題,可以通過搭建消息服務(wù)集群來解決。消息中間件集群有多種實(shí)現(xiàn)方式,下面以ActiveMQ為例,通過Zookeeper搭建集群,消息服務(wù)集群如圖9所示。
各ActiveMQ服務(wù)器會向Zookeeper注冊,Zookeeper會為每個ActiveMQ服務(wù)器分配序列號,其中序列號最小的為主用服務(wù)器。生產(chǎn)者和消費(fèi)者連接消息隊(duì)列服務(wù)器時,會首先從Zookeeper獲取當(dāng)前主用服務(wù)器地址再進(jìn)行后續(xù)訪問操作。當(dāng)主用服務(wù)器失效,Zookeeper會檢測到并刪除失效節(jié)點(diǎn),然后選取當(dāng)前最小序號的服務(wù)器升級為主用服務(wù)器,同時通知所有客戶端,以完成主備切換,保證消息服務(wù)的高可用性。
3.2復(fù)雜性提高
系統(tǒng)引入消息中間件后,消息可能會被重復(fù)消費(fèi)或者消息可能在消費(fèi)前丟失。為了保證消息沒有被重復(fù)消費(fèi)和處理消息丟失的情況,系統(tǒng)復(fù)雜性不可避免地會有提高。
(1)消息重復(fù)問題
解決思路是增加一張消費(fèi)信息表,給所有消息分配全局唯一的標(biāo)識用作唯一索引。消費(fèi)者開始消費(fèi)之前,先去消費(fèi)信息表中查詢有沒有匹配的消費(fèi)記錄,如果有就說明已經(jīng)消費(fèi)過,不再處理,消息重復(fù)解決流程如圖10所示。
(2)消息丟失問題
解決思路是增加一張消息發(fā)送記錄表,當(dāng)生產(chǎn)者發(fā)完消息后,往該表寫入一條數(shù)據(jù),其狀態(tài)標(biāo)記為待確認(rèn)。如果消費(fèi)者讀取消息后,調(diào)用生產(chǎn)者的接口更新該消息的狀態(tài)為已確認(rèn)。另外新增一個任務(wù),定時檢查消息發(fā)送表,如果一定時間間隔后,仍有待確認(rèn)狀態(tài)的消息,則認(rèn)為該消息已經(jīng)丟失,并重新發(fā)送該消息,消息丟失處理流程如圖11所示。
3.3數(shù)據(jù)一致性
消息隊(duì)列帶來的異步可以提高系統(tǒng)響應(yīng)速度,但是萬一消息消費(fèi)者在處理消息過程中沒有正確處理,會導(dǎo)致數(shù)據(jù)不一致的情況出現(xiàn)。解決思路是通過在業(yè)務(wù)邏輯中增加重試機(jī)制來保持最終一致性。
對于消息量較小的業(yè)務(wù)場景,可以采用同步重試。在消費(fèi)消息時如果處理失敗,立刻重試若干次。如果還是失敗,則寫入到記錄表中。如果消息量較大,不建議采用同步重試方式。如果出現(xiàn)網(wǎng)絡(luò)異常,可能會導(dǎo)致大量的消息不斷重試,影響消息讀取速度,造成消息堆積。對于消息量較大的業(yè)務(wù)場景,可以采用異步重試。在消費(fèi)者處理失敗之后,立刻寫入重試表,建立另外一個任務(wù),定時讀取重試表進(jìn)行處理,數(shù)據(jù)一致性處理流程如圖12所示。
本文以在Java主流開發(fā)的SpringBoot工程中引入ActiveMQ消息中間件為例,建立了一個消息生產(chǎn)者工程和一個消息消費(fèi)者工程,同時在本機(jī)中運(yùn)行5.7.0版本的ActiveMQ程序。
由上述代碼示例可以發(fā)現(xiàn),在SpringBoot工程中引入ActiveMQ消息中間件僅需要增加少量的代碼。
消息中間件為應(yīng)用系統(tǒng)提供了高效、可靠的消息通信手段,能夠很好地實(shí)現(xiàn)異構(gòu)平臺之間的信息交互。消息中間件以其獨(dú)特的優(yōu)勢為各種分布式應(yīng)用的開發(fā)注入了強(qiáng)大的活力,極大地推動了應(yīng)用系統(tǒng)集成和發(fā)展。
[1]陳安林.基于JMS的消息中間件的研究與實(shí)現(xiàn)[D].哈爾濱:哈爾濱工程大學(xué),2006.
[2]朱方娥,曹寶香.基于JMS的消息隊(duì)列中間件的研究與實(shí)現(xiàn)[J].計算機(jī)技術(shù)與發(fā)展,2008(5):172-175.
[3]孫弋,溫迅.一種面向消息的中間件的設(shè)計與實(shí)現(xiàn)[J].物聯(lián)網(wǎng)技術(shù),2019,9(3):81-84.
[4] AHUJA S P, MUPPARAJU N.Performance Evaluation and Comparison of Distributed Messaging using Message Oriented Middleware[J].Computer and Information Science, 2014,7(4):9-20.
[5]王小霞,陳亮.一種消息隊(duì)列中間件的設(shè)計與實(shí)現(xiàn)[J].計算機(jī)工程,2006(21):81-83.
[6] TIMOTHY B.Instant Apache ActiveMQ Messaging Application Development[M].Birmingham:Packt Publishing, 2013.