談?wù)勏⒖偩€跟消息隊(duì)列的區(qū)別,以及對(duì)于企業(yè)級(jí)應(yīng)用需要將消息隊(duì)列封裝成消息總線的必要性。
消息總線跟消息隊(duì)列有何區(qū)別?如果有人問(wèn)你這個(gè)問(wèn)題,你的答案是什么?如果你的消息總線是基于一個(gè)已經(jīng)相當(dāng)成熟的消息隊(duì)列或者消息系統(tǒng)做二次封裝。為什么需要用你的客戶端,而不直接用原始的(這是一個(gè)大家都相信權(quán)威的時(shí)代,請(qǐng)注意這里用的是相信,而不是迷信,你確實(shí)應(yīng)該相信權(quán)威,至少比相信一個(gè)新手來(lái)得靠譜,當(dāng)然我這里指的權(quán)威,是正面的意思)?
那么我從以下幾點(diǎn)來(lái)談?wù)勎覍?duì)這個(gè)問(wèn)題的思考:
消息隊(duì)列clientAPI權(quán)限太大,clientAPI信任級(jí)別太高
消息隊(duì)列clientAPI面向技術(shù),消息總線clientAPI面向技術(shù)+業(yè)務(wù)
消息隊(duì)列無(wú)法隱藏通信細(xì)節(jié)
消息隊(duì)列無(wú)法實(shí)施實(shí)時(shí)管控
總線的優(yōu)勢(shì):統(tǒng)一入口,簡(jiǎn)化攔截成本
這里為了理解簡(jiǎn)單,你就暫且先把RabbitMQ當(dāng)做是個(gè)消息隊(duì)列,其實(shí)它不只是個(gè)消息隊(duì)列,其他的一些基于JMS的消息隊(duì)列對(duì)于回答這個(gè)問(wèn)題而言,也能成立。
(1)消息隊(duì)列clientAPI權(quán)限太大,信任級(jí)別太高
這一點(diǎn)不僅僅是哪一個(gè)服務(wù)端組件的客戶端driver的實(shí)現(xiàn)是這樣,絕大部分其實(shí)都是這樣的,它們的client其實(shí)是對(duì)服務(wù)端組件(或者稱之為服務(wù)器)協(xié)議的翻譯。這些服務(wù)器大都帶有commandline interface(這幾乎是標(biāo)配)。其實(shí),CLI跟在程序中使用的各種語(yǔ)言的client庫(kù)沒(méi)有區(qū)別本質(zhì)區(qū)別,它們相對(duì)于server而言都是client——都是對(duì)server實(shí)現(xiàn)的protocol的翻譯或者轉(zhuǎn)換,而這些API都是對(duì)這些包裝過(guò)的協(xié)議的調(diào)用。因此它們都存在一些“management”形式的接口:比如create,delete,remove某個(gè)component之類的。沒(méi)錯(cuò),你去看所有帶client的組件的實(shí)現(xiàn),它們都包含了這些API(這不是對(duì)錯(cuò)的問(wèn)題,這些client本身就沒(méi)有也不應(yīng)該假設(shè)你的使用場(chǎng)景)。比如你看看redis的client:jedis——它甚至具備了flushAll,flushDB的功能(清空所有redis數(shù)據(jù)),除了能關(guān)閉server還有什么事它不能做?而就RabbitMQ而言,它的officialnative java client,可以創(chuàng)建/刪除其通信的核心組件:exchange,queue。你能直接將這些client散布到各個(gè)業(yè)務(wù)系統(tǒng)里去而不加阻攔?你當(dāng)然有必要做二次封裝以移除這些高危的managementAPI。
(2)消息隊(duì)列clientAPI面向技術(shù)而消息總線clientAPI面向技術(shù)+業(yè)務(wù)
消息隊(duì)列的clientAPI大都面向協(xié)議、通信實(shí)現(xiàn),面向可用性以及高性能,如果歸類一下那就是面向技術(shù),除了通信場(chǎng)景它不會(huì)去模擬業(yè)務(wù)場(chǎng)景。而消息總線需要帶著業(yè)務(wù)場(chǎng)景去實(shí)現(xiàn)需要支持的機(jī)制。
當(dāng)你去搜索任何一個(gè)消息隊(duì)列的時(shí)候,它的advantage里都有一條:生產(chǎn)者與消費(fèi)者解耦,就像下面這樣:
就生產(chǎn)者跟消費(fèi)者模型而言,這確實(shí)是消息隊(duì)列的優(yōu)勢(shì)。不過(guò)這種優(yōu)勢(shì)也被限制在一些特定的使用場(chǎng)景下,比如:?jiǎn)我粯I(yè)務(wù)的消息排隊(duì)處理。因此通用消息隊(duì)列的場(chǎng)景更適用于單一職責(zé)的生產(chǎn)者跟消費(fèi)者模型;而我們期待的消息總線卻是企業(yè)里各個(gè)系統(tǒng)中消息的通信,側(cè)重點(diǎn)在于通信上。消息隊(duì)列只是提供了一種非常適合于消息通信的實(shí)現(xiàn)機(jī)制(消息有序,消息緩存等),因此消息總線是在消息隊(duì)列提供的技術(shù)支撐上封裝出適合消息交互的業(yè)務(wù)場(chǎng)景。
(3)消息隊(duì)列無(wú)法隱藏通信細(xì)節(jié)
對(duì)于企業(yè)內(nèi)的系統(tǒng)交互,我們希望它盡可能保證數(shù)據(jù)的安全性。而數(shù)據(jù)通常都暫存在隊(duì)列中,因此保證數(shù)據(jù)的安全性就順其自然得轉(zhuǎn)變成保證消息隊(duì)列訪問(wèn)的安全性:你總是不應(yīng)該讓沒(méi)有經(jīng)過(guò)授權(quán)的客戶端去訪問(wèn)本不應(yīng)該訪問(wèn)到的隊(duì)列。可惜的是RabbitMQ官方的客戶端達(dá)不到這種要求,它要訪問(wèn)一個(gè)隊(duì)列,需要知道真實(shí)隊(duì)列的名稱,需要知道其路由路徑。而就連接一個(gè)隊(duì)列而言,我們認(rèn)為它提供了太多的信息,但這是沒(méi)辦法的事情,因?yàn)樗膃xchange以及queue的混搭機(jī)制非常靈活,所以你得提供一個(gè)稱之為routingkey的路由路徑。而不管怎樣,如果你把這個(gè)信息開放給調(diào)用端去填寫,幾乎肯定會(huì)暴露你服務(wù)端exchange以及queue的路由機(jī)制以及拓?fù)浣Y(jié)構(gòu)。因此我們需要做什么?我們需要找到一種通信機(jī)制,讓它對(duì)外只需要知道有個(gè)proxy節(jié)點(diǎn),而不需要去關(guān)注真實(shí)的queue的名稱;然后想一個(gè)辦法把其routingkey隱藏在消息總線內(nèi)部。
(4)消息隊(duì)列無(wú)法實(shí)施實(shí)時(shí)管控
如果你在企業(yè)內(nèi)各個(gè)系統(tǒng)之間引入消息總線,很顯然訪問(wèn)控制是必須提供的。比如對(duì)某個(gè)隊(duì)列實(shí)施消息大小限制,激活/禁用某個(gè)隊(duì)列等。
之前我們提到過(guò)消息隊(duì)列不是面向業(yè)務(wù)的,它自身沒(méi)有過(guò)多得考慮數(shù)據(jù)的安全性以及對(duì)訪問(wèn)的安全控制機(jī)制。而且我們也幾乎很難去改造一個(gè)消息隊(duì)列的服務(wù)端實(shí)現(xiàn),除非它是基于攔截器/插件模式的。即便RabbitMQ是支持插件的,但對(duì)于erlang這樣一個(gè)受眾不是特別廣泛的語(yǔ)言,你去給它寫插件一不小心就會(huì)走到坑里去,并且RabbitMQ官方也已經(jīng)申明了它們十分不建議你自己去編寫插件。考慮到諸多不便,我們只能在客戶端上做文章。毫無(wú)疑問(wèn),我們的實(shí)時(shí)管控信息還是必須存儲(chǔ)在服務(wù)端(只是它是一個(gè)獨(dú)立的服務(wù)端),但原生的client很顯然是不支持這種機(jī)制的,因此我們需要在原生client外部封裝訂閱實(shí)時(shí)管控信息以及實(shí)施訪問(wèn)控制的邏輯代碼。
(5)總線的優(yōu)勢(shì):統(tǒng)一入口,簡(jiǎn)化攔截成本
無(wú)論是消息總線還是服務(wù)總線,其實(shí)所謂的總線就是進(jìn)行先收攏再發(fā)散的過(guò)程。先收攏,從統(tǒng)一的入口進(jìn)去,完成必要的統(tǒng)一處理邏輯;再發(fā)散,按照路由規(guī)則,路由到各個(gè)組件去處理。事實(shí)上這就是代理的作用:屏蔽內(nèi)部細(xì)節(jié),對(duì)外統(tǒng)一入口。在基于代理的基礎(chǔ)上,我們可以對(duì)消息總線上所有的消息做日志記錄(因?yàn)樗邢⒌耐ㄐ哦急仨毥?jīng)過(guò)代理),并且還是在不切斷RabbitMQ自身Channel的基礎(chǔ)上,而如果想在路由上實(shí)現(xiàn)一個(gè)Proxy,那基本上離不開一個(gè)樹形拓?fù)浣Y(jié)構(gòu)。
寫在最后
這篇主要談了消息總線跟消息隊(duì)列的區(qū)別。其實(shí)市面上已經(jīng)有一些成熟的消息隊(duì)列可以開箱即用,如果你針對(duì)消息隊(duì)列來(lái)封裝出一個(gè)消息總線,總有人會(huì)認(rèn)為是否有這個(gè)必要性。如果沒(méi)有這些開源的消息隊(duì)列,那么完全有你自己來(lái)實(shí)現(xiàn)消息總線的話,你還是需要實(shí)現(xiàn)出一個(gè)跟市面上類似的MQ或者M(jìn)essageBroker(見POSA卷4),因此消息隊(duì)列只是實(shí)現(xiàn)消息總線的基礎(chǔ),或者是它的消息通信方式;而選擇基于一些成熟的MessageBroker來(lái)進(jìn)行開發(fā),既能省去很多的工作量,又能享有它們提供的穩(wěn)定性以及社區(qū)的貢獻(xiàn)。
評(píng)論
查看更多