打算最近寫(xiě)一系列的Rabbitmq的博客,很詳盡的講解MQ的方方面面,也便于自己以后在工作中回頭來(lái)總結(jié)整理。
為什么要使用消息隊(duì)列
- 異步處理
- 系統(tǒng)解耦
解耦是消息中間隊(duì)列解決的最本質(zhì)問(wèn)題。所謂解耦,簡(jiǎn)單一點(diǎn)就是一個(gè)事務(wù),只關(guān)心核心的流程。而需要依賴(lài)其他系統(tǒng)但不那么重要的事情,有通知即可,無(wú)需等待結(jié)果。換句話(huà)說(shuō),關(guān)心的是“通知,而非“處理”。
比如下單過(guò)程中,需要發(fā)送短信積分,如果下游系統(tǒng)過(guò)慢(比如短信網(wǎng)關(guān)速度不好),主流程一直在等待。用戶(hù)肯定不希望支付下單的過(guò)程中幾分鐘之后才得到結(jié)果。那么我們只需要通知短信系統(tǒng)“我們支付成功了”,不一定非要等待它處理完成。 - 流量削峰
試想上下游對(duì)于事件的處理能力是不同的。比如,Web前端每秒承受上千萬(wàn)的請(qǐng)求,并不是什么神奇的事情,只需要加多一點(diǎn)機(jī)器,再搭建一些LVS負(fù)載均衡設(shè)備和Nginx等即可。但數(shù)據(jù)庫(kù)的處理能力卻十分有限,即使使用SSD加分庫(kù)分表,單機(jī)的處理能力仍然在萬(wàn)級(jí)。由于成本的考慮,我們不能奢求數(shù)據(jù)庫(kù)的機(jī)器數(shù)量追上前端。
這種問(wèn)題同樣存在于系統(tǒng)和系統(tǒng)之間,如短信系統(tǒng)可能由于短板效應(yīng),速度卡在網(wǎng)關(guān)上(每秒幾百次請(qǐng)求),跟前端的并發(fā)量不是一個(gè)數(shù)量級(jí)。但用戶(hù)晚上個(gè)半分鐘左右收到短信,一般是不會(huì)有太大問(wèn)題的。如果沒(méi)有消息隊(duì)列,兩個(gè)系統(tǒng)之間通過(guò)協(xié)商、滑動(dòng)窗口等復(fù)雜的方案也不是說(shuō)不能實(shí)現(xiàn)。但系統(tǒng)復(fù)雜性指數(shù)級(jí)增長(zhǎng),勢(shì)必在上游或者下游做存儲(chǔ),并且要處理定時(shí)、擁塞等一系列問(wèn)題。而且每當(dāng)有處理能力有差距的時(shí)候,都需要單獨(dú)開(kāi)發(fā)一套邏輯來(lái)維護(hù)這套邏輯。所以,利用中間系統(tǒng)轉(zhuǎn)儲(chǔ)兩個(gè)系統(tǒng)的通信內(nèi)容,并在下游系統(tǒng)有能力處理這些消息的時(shí)候,再處理這些消息,是一套相對(duì)較通用的方式。 - 廣播
消息隊(duì)列的基本功能之一是進(jìn)行廣播。如果沒(méi)有消息隊(duì)列,每當(dāng)一個(gè)新的業(yè)務(wù)方接入,我們都要聯(lián)調(diào)一次新接口。有了消息隊(duì)列,我們只需要關(guān)心消息是否送達(dá)了隊(duì)列,至于誰(shuí)希望訂閱,是下游的事情,無(wú)疑極大地減少了開(kāi)發(fā)和聯(lián)調(diào)的工作量。 - 最終一致性
最終一致性指的是兩個(gè)系統(tǒng)的狀態(tài)保持一致,要么都成功,要么都失敗。當(dāng)然有個(gè)時(shí)間限制,理論上越快越好,但實(shí)際上在各種異常的情況下,可能會(huì)有一定延遲達(dá)到最終一致?tīng)顟B(tài),但最后兩個(gè)系統(tǒng)的狀態(tài)是一樣的。
業(yè)界有一些為“最終一致性”而生的消息隊(duì)列,如Notify(阿里)、QMQ(去哪兒)等,其設(shè)計(jì)初衷,就是為了交易系統(tǒng)中的高可靠通知。
本地事務(wù)維護(hù)業(yè)務(wù)變化和通知消息,一起落地(失敗則一起回滾),然后RPC到達(dá)broker,在broker成功落地后,RPC返回成功,本地消息可以刪除。否則本地消息一直靠定時(shí)任務(wù)輪詢(xún)不斷重發(fā),這樣就保證了消息可靠落地broker。
broker往consumer發(fā)送消息的過(guò)程類(lèi)似,一直發(fā)送消息,直到consumer發(fā)送消費(fèi)成功確認(rèn)。
總結(jié):
消息隊(duì)列不是萬(wàn)能的。對(duì)于需要強(qiáng)事務(wù)保證而且延遲敏感的,RPC是優(yōu)于消息隊(duì)列的。
對(duì)于一些無(wú)關(guān)痛癢,或者對(duì)于別人非常重要但是對(duì)于自己不是那么關(guān)心的事情,可以利用消息隊(duì)列去做。
支持最終一致性的消息隊(duì)列,能夠用來(lái)處理延遲不那么敏感的“分布式事務(wù)”場(chǎng)景,而且相對(duì)于笨重的分布式事務(wù),可能是更優(yōu)的處理方式。
當(dāng)上下游系統(tǒng)處理能力存在差距的時(shí)候,利用消息隊(duì)列做一個(gè)通用的“漏斗”。在下游有能力處理的時(shí)候,再進(jìn)行分發(fā)。
為什么要選擇RabbitMQ
- 基于AMQP協(xié)議
- 高并發(fā)
- 高性能
- 高可用
- 強(qiáng)大的社區(qū)支持,以及很多公司都在使用
- 支持插件
- 支持多語(yǔ)言
AMQP協(xié)議介紹
出現(xiàn)背景
越是大型的公司越是不可避免的使用來(lái)自眾多供應(yīng)商的MQ產(chǎn)品,來(lái)服務(wù)企業(yè)內(nèi)部的不同應(yīng)用。如果應(yīng)用已經(jīng)訂閱了TIBCO MQ信息,若突然需要消費(fèi)來(lái)自IBM MQ的消息,則實(shí)現(xiàn)起來(lái)會(huì)非常困難。這些產(chǎn)品使用不同的api,不同的協(xié)議,因而毫無(wú)疑問(wèn)無(wú)法聯(lián)合起來(lái)組成單一的總線(xiàn)。為了解決這個(gè)問(wèn)題,Java Message Service(JMS)在2001年誕生了。JMS試圖通過(guò)提供公共java api的方式,隱藏單獨(dú)MQ產(chǎn)品供應(yīng)商提供的實(shí)際接口,從而跨越了壁壘和解決了互通問(wèn)題。從技術(shù)上講,java應(yīng)用程序只需要對(duì)JMS API編程,選擇合適的MQ驅(qū)動(dòng)即可。JMS會(huì)打理好其他部分的。問(wèn)題是你在嘗試使用單獨(dú)編準(zhǔn)化接口來(lái)整合眾多不同的接口。這就像是把不同的類(lèi)型的衣服粘在一起:縫合處終究會(huì)裂開(kāi)。使用JMS(Java Message Service)的應(yīng)用程序會(huì)變得更加脆弱。我們需要新的消息通信標(biāo)準(zhǔn)化方案。
高級(jí)消息隊(duì)列協(xié)議(AMQP)是面向消息的中間件的開(kāi)放標(biāo)準(zhǔn)應(yīng)用層協(xié)議。 AMQP的特征是消息導(dǎo)向,排隊(duì),路由(包括點(diǎn)對(duì)點(diǎn)和發(fā)布和訂閱),可靠性和安全性。
AMQP要求消息傳遞提供商和客戶(hù)端的行為在不同供應(yīng)商實(shí)現(xiàn)可互操作的情況下,以與SMTP,HTTP,F(xiàn)TP等相同的方式創(chuàng)建了可互操作的系統(tǒng)。 中間件的以前標(biāo)準(zhǔn)化發(fā)生在API級(jí)別(例如JMS),并且專(zhuān)注于使程序員與不同中間件實(shí)現(xiàn)的交互標(biāo)準(zhǔn)化,而不是提供多個(gè)實(shí)現(xiàn)之間(AMQP的實(shí)現(xiàn))的互操作性。與定義API和消息傳遞實(shí)現(xiàn)必須提供的一組行為的JMS不同,AMQP是線(xiàn)級(jí)協(xié)議。 線(xiàn)級(jí)協(xié)議是以網(wǎng)絡(luò)流作為字節(jié)流發(fā)送的數(shù)據(jù)格式的描述。 因此,無(wú)論實(shí)現(xiàn)語(yǔ)言如何,任何可以創(chuàng)建和解釋符合此數(shù)據(jù)格式的消息的工具都可以與任何其他兼容工具進(jìn)行互操作。
AMQP協(xié)議是具有現(xiàn)代特征的二進(jìn)制協(xié)議。一個(gè)提供統(tǒng)一消息服務(wù)的應(yīng)用層標(biāo)準(zhǔn)高級(jí)消息隊(duì)列協(xié)議,是應(yīng)用層協(xié)議的一個(gè)開(kāi)發(fā)標(biāo)準(zhǔn),為面向消息的中間件設(shè)計(jì)?;诖藚f(xié)議的客戶(hù)端與消息中間件可傳遞消息,并不受客戶(hù)端/中間件不同產(chǎn)品,不同開(kāi)發(fā)語(yǔ)言等條件的限制。
AMQP是一種二進(jìn)制應(yīng)用層協(xié)議,旨在有效地支持各種消息應(yīng)用和通信模式。 它提供流控制的面向消息的通信,其中包括消息傳遞保證,例如最多一次(每個(gè)消息被投遞一次或從不投遞消息),至少一次(每個(gè)消息肯定要被傳遞,但可以在不同的時(shí)間)和確定一次(其中消息將始終確定到達(dá)并僅執(zhí)行一次),以及基于SASL和/或TLS的身份驗(yàn)證和/或加密。 它假定一個(gè)基本的可靠傳輸層協(xié)議,如傳輸控制協(xié)議(TCP)。
AMQP規(guī)范定義在幾個(gè)層次中:(i)類(lèi)型系統(tǒng)(傳遞的消息類(lèi)型),(ii)用于將消息從一個(gè)進(jìn)程轉(zhuǎn)移到另一個(gè)進(jìn)程的對(duì)稱(chēng)異步協(xié)議,(iii)標(biāo)準(zhǔn)的可擴(kuò)展消息格式(iv)一系列的標(biāo)準(zhǔn)化但可擴(kuò)展的“消息傳遞功能”。
一些概念梳理
- Server:又稱(chēng)為Broker。接收客戶(hù)端連接,實(shí)現(xiàn)AMQP的服務(wù)器實(shí)體。
- Connection:連接,應(yīng)用程序與Broker的網(wǎng)絡(luò)連接。
- Channel:信道,幾乎所有的操作都在Channel中進(jìn)行,Channel是進(jìn)行消息讀寫(xiě)的通道??蛻?hù)端可建立多個(gè)Channel,每個(gè)Channel代表一個(gè)會(huì)話(huà)任務(wù)。
- Message:消息。服務(wù)器和應(yīng)用程序之間傳遞的數(shù)據(jù),本質(zhì)上就是一段數(shù)據(jù),由Properties和Body組成。
- Exchange:交換機(jī)。接收消息,根據(jù)路由鍵轉(zhuǎn)發(fā)消息到綁定的隊(duì)列。
- Binding:Exchange和Queue之間的虛擬連接,binding中可以包含routing key。
- Routing key:一個(gè)虛擬地址,虛擬機(jī)可用它來(lái)確定如何路由一個(gè)特定消息。
- Queue:也稱(chēng)為Message Queue,消息隊(duì)列,保存消息并將它們轉(zhuǎn)發(fā)給消費(fèi)者。
- Virtual Host:其實(shí)是一個(gè)虛擬概念。類(lèi)似于權(quán)限控制組,一個(gè)Virtual Host里面可以有若干個(gè)Exchange和Queue,可以用來(lái)隔離Exchange和Queue。,同一個(gè)Virtual Host里面不能有相同名稱(chēng)的Exchange和Queue。但是權(quán)限控制的最小粒度是Virtual Host。(下面會(huì)講到)

總結(jié):
生產(chǎn)者將消息發(fā)送到Exchange交換機(jī)的,不是發(fā)送到Queue上的,生產(chǎn)者不知道消息是誰(shuí)消費(fèi),有哪些消費(fèi)者消費(fèi)。Exchange根據(jù)一定的路由規(guī)則將消息轉(zhuǎn)發(fā)到Queue。
消費(fèi)者是監(jiān)聽(tīng)隊(duì)列的,不知道是哪個(gè)生產(chǎn)者發(fā)送的。
AMQP我的理解
一個(gè)開(kāi)放的面向消息中間件的協(xié)議,所有此協(xié)議的實(shí)現(xiàn)可以進(jìn)行互相操作,無(wú)論實(shí)現(xiàn)語(yǔ)言如何,任何符合此協(xié)議的數(shù)據(jù)格式的消息工具都可以與任何其他兼容工具進(jìn)行互操作。而以前JMS(Java Message Service),將不同的中間件的實(shí)現(xiàn)進(jìn)行API層次的標(biāo)準(zhǔn)化。