本文地址:http://www.host900.com/index.php/articles/351/
介紹RabbitMQ前篙螟,有必須先了解一下AMQP協(xié)議赫模。AMQP協(xié)議是一個(gè)高級抽象層消息通信協(xié)議潜支,RabbitMQ是AMQP協(xié)議的實(shí)現(xiàn)丐吓。它主要包括以下組件:
1. Server(broker):?接受客戶端連接,實(shí)現(xiàn)AMQP消息隊(duì)列和路由功能的進(jìn)程趟据。
2.?Virtual Host:其實(shí)是一個(gè)虛擬概念券犁,類似于權(quán)限控制組,一個(gè)Virtual Host里面可以有若干個(gè)Exchange和Queue汹碱,但是權(quán)限控制的最小粒度是Virtual Host
3.Exchange:接受生產(chǎn)者發(fā)送的消息粘衬,并根據(jù)Binding規(guī)則將消息路由給服務(wù)器中的隊(duì)列。ExchangeType決定了Exchange路由消息的行為咳促,例如稚新,在RabbitMQ中,ExchangeType有direct跪腹、Fanout和Topic三種褂删,不同類型的Exchange路由的行為是不一樣的。
4.Message Queue:消息隊(duì)列冲茸,用于存儲還未被消費(fèi)者消費(fèi)的消息屯阀。
5.Message:?由Header和Body組成,Header是由生產(chǎn)者添加的各種屬性的集合轴术,包括Message是否被持久化难衰、由哪個(gè)Message Queue接受、優(yōu)先級是多少等膳音。而Body是真正需要傳輸?shù)腁PP數(shù)據(jù)召衔。
6.Binding:Binding聯(lián)系了Exchange與Message Queue。Exchange在與多個(gè)Message Queue發(fā)生Binding后會生成一張路由表祭陷,路由表中存儲著Message Queue所需消息的限制條件即Binding Key。當(dāng)Exchange收到Message時(shí)會解析其Header得到Routing Key趣席,Exchange根據(jù)Routing Key與Exchange Type將Message路由到Message Queue兵志。Binding Key由Consumer在Binding Exchange與Message Queue時(shí)指定,而Routing Key由Producer發(fā)送Message時(shí)指定宣肚,兩者的匹配方式由Exchange Type決定想罕。
7.Connection:連接,對于RabbitMQ而言霉涨,其實(shí)就是一個(gè)位于客戶端和Broker之間的TCP連接按价。
8.Channel:信道,僅僅創(chuàng)建了客戶端到Broker之間的連接后笙瑟,客戶端還是不能發(fā)送消息的楼镐。需要為每一個(gè)Connection創(chuàng)建Channel,AMQP協(xié)議規(guī)定只有通過Channel才能執(zhí)行AMQP的命令往枷。一個(gè)Connection可以包含多個(gè)Channel框产。之所以需要Channel凄杯,是因?yàn)門CP連接的建立和釋放都是十分昂貴的,如果一個(gè)客戶端每一個(gè)線程都需要與Broker交互秉宿,如果每一個(gè)線程都建立一個(gè)TCP連接戒突,暫且不考慮TCP連接是否浪費(fèi),就算操作系統(tǒng)也無法承受每秒建立如此多的TCP連接描睦。RabbitMQ建議客戶端線程之間不要共用Channel膊存,至少要保證共用Channel的線程發(fā)送消息必須是串行的,但是建議盡量共用Connection忱叭。
9.Command:AMQP的命令膝舅,客戶端通過Command完成與AMQP服務(wù)器的交互來實(shí)現(xiàn)自身的邏輯。例如在RabbitMQ中窑多,客戶端可以通過publish命令發(fā)送消息仍稀,txSelect開啟一個(gè)事務(wù),txCommit提交一個(gè)事務(wù)埂息。
在了解了AMQP模型以后技潘,需要簡單介紹一下AMQP的協(xié)議棧,AMQP協(xié)議本身包括三層:
1.???????Modle Layer千康,位于協(xié)議最高層享幽,主要定義了一些供客戶端調(diào)用的命令,客戶端可以利用這些命令實(shí)現(xiàn)自己的業(yè)務(wù)邏輯拾弃,例如值桩,客戶端可以通過queue.declare聲明一個(gè)隊(duì)列,利用consume命令獲取一個(gè)隊(duì)列中的消息豪椿。
2.???????Session Layer奔坟,主要負(fù)責(zé)將客戶端的命令發(fā)送給服務(wù)器,在將服務(wù)器端的應(yīng)答返回給客戶端搭盾,主要為客戶端與服務(wù)器之間通信提供可靠性咳秉、同步機(jī)制和錯(cuò)誤處理。
3.???????Transport Layer鸯隅,主要傳輸二進(jìn)制數(shù)據(jù)流澜建,提供幀的處理、信道復(fù)用蝌以、錯(cuò)誤檢測和數(shù)據(jù)表示炕舵。
從AMQP協(xié)議可以看出,MessageQueue跟畅、Exchange和Binding構(gòu)成了AMQP協(xié)議的核心咽筋,下面我們就圍繞這三個(gè)主要組件 ?? 從應(yīng)用使用的角度全面的介紹如何利用Rabbit MQ構(gòu)建消息隊(duì)列以及使用過程中的注意事項(xiàng)。
1.?聲明MessageQueue
在Rabbit MQ中碍彭,無論是生產(chǎn)者發(fā)送消息還是消費(fèi)者接受消息晤硕,都首先需要聲明一個(gè)MessageQueue悼潭。這就存在一個(gè)問題,是生產(chǎn)者聲明還是消費(fèi)者聲明呢舞箍?要解決這個(gè)問題舰褪,首先需要明確:
a)消費(fèi)者是無法訂閱或者獲取不存在的MessageQueue中信息。
b)消息被Exchange接受以后疏橄,如果沒有匹配的Queue占拍,則會被丟棄。
在明白了上述兩點(diǎn)以后捎迫,就容易理解如果是消費(fèi)者去聲明Queue晃酒,就有可能會出現(xiàn)在聲明Queue之前,生產(chǎn)者已發(fā)送的消息被丟棄的隱患窄绒。如果應(yīng)用能夠通過消息重發(fā)的機(jī)制允許消息丟失贝次,則使用此方案沒有任何問題。但是如果不能接受該方案彰导,這就需要無論是生產(chǎn)者還是消費(fèi)者蛔翅,在發(fā)送或者接受消息前,都需要去嘗試建立消息隊(duì)列位谋。這里有一點(diǎn)需要明確山析,如果客戶端嘗試建立一個(gè)已經(jīng)存在的消息隊(duì)列,Rabbit MQ不會做任何事情掏父,并返回客戶端建立成功的笋轨。
如果一個(gè)消費(fèi)者在一個(gè)信道中正在監(jiān)聽某一個(gè)隊(duì)列的消息,Rabbit MQ是不允許該消費(fèi)者在同一個(gè)channel去聲明其他隊(duì)列的赊淑。Rabbit MQ中爵政,可以通過queue.declare命令聲明一個(gè)隊(duì)列,可以設(shè)置該隊(duì)列以下屬性:
a) Exclusive:排他隊(duì)列膏燃,如果一個(gè)隊(duì)列被聲明為排他隊(duì)列茂卦,該隊(duì)列僅對首次聲明它的連接可見,并在連接斷開時(shí)自動刪除组哩。這里需要注意三點(diǎn):其一,排他隊(duì)列是基于連接可見的处渣,同一連接的不同信道是可以同時(shí)訪問同一個(gè)連接創(chuàng)建的排他隊(duì)列的伶贰。其二,“首次”罐栈,如果一個(gè)連接已經(jīng)聲明了一個(gè)排他隊(duì)列黍衙,其他連接是不允許建立同名的排他隊(duì)列的,這個(gè)與普通隊(duì)列不同荠诬。其三琅翻,即使該隊(duì)列是持久化的位仁,一旦連接關(guān)閉或者客戶端退出,該排他隊(duì)列都會被自動刪除的方椎。這種隊(duì)列適用于只限于一個(gè)客戶端發(fā)送讀取消息的應(yīng)用場景聂抢。
b)?? Auto-delete:自動刪除,如果該隊(duì)列沒有任何訂閱的消費(fèi)者的話棠众,該隊(duì)列會被自動刪除琳疏。這種隊(duì)列適用于臨時(shí)隊(duì)列。
c)?? Durable:持久化闸拿,這個(gè)會在后面作為專門一個(gè)章節(jié)討論空盼。
d)??其他選項(xiàng),例如如果用戶僅僅想查詢某一個(gè)隊(duì)列是否已存在新荤,如果不存在揽趾,不想建立該隊(duì)列,仍然可以調(diào)用queue.declare苛骨,只不過需要將參數(shù)passive設(shè)為true篱瞎,傳給queue.declare,如果該隊(duì)列已存在智袭,則會返回true奔缠;如果不存在,則會返回Error吼野,但是不會創(chuàng)建新的隊(duì)列校哎。
2. 生產(chǎn)者發(fā)送消息
在AMQP模型中,Exchange是接受生產(chǎn)者消息并將消息路由到消息隊(duì)列的關(guān)鍵組件瞳步。ExchangeType和Binding決定了消息的路由規(guī)則闷哆。所以生產(chǎn)者想要發(fā)送消息,首先必須要聲明一個(gè)Exchange和該Exchange對應(yīng)的Binding单起”д可以通過 ExchangeDeclare和BindingDeclare完成。在Rabbit MQ中嘀倒,聲明一個(gè)Exchange需要三個(gè)參數(shù):ExchangeName屈留,ExchangeType和Durable。ExchangeName是該Exchange的名字测蘑,該屬性在創(chuàng)建Binding和生產(chǎn)者通過publish推送消息時(shí)需要指定灌危。ExchangeType,指Exchange的類型碳胳,在RabbitMQ中勇蝙,有三種類型的Exchange:direct ,fanout和topic挨约,不同的Exchange會表現(xiàn)出不同路由行為味混。Durable是該Exchange的持久化屬性产雹,這個(gè)會在消息持久化章節(jié)討論。聲明一個(gè)Binding需要提供一個(gè)QueueName翁锡,ExchangeName和BindingKey蔓挖。下面我們就分析一下不同的ExchangeType表現(xiàn)出的不同路由規(guī)則。
生產(chǎn)者在發(fā)送消息時(shí)盗誊,都需要指定一個(gè)RoutingKey和Exchange时甚,Exchange在接到該RoutingKey以后,會判斷該ExchangeType:
a) 如果是Direct類型哈踱,則會將消息中的RoutingKey與該Exchange關(guān)聯(lián)的所有Binding中的BindingKey進(jìn)行比較荒适,如果相等,則發(fā)送到該Binding對應(yīng)的Queue中开镣。
b)??如果是?Fanout?類型刀诬,則會將消息發(fā)送給所有與該?Exchange?定義過?Binding?的所有?Queues?中去,其實(shí)是一種廣播行為邪财。
c)如果是Topic類型陕壹,則會按照正則表達(dá)式,對RoutingKey與BindingKey進(jìn)行匹配树埠,如果匹配成功糠馆,則發(fā)送到對應(yīng)的Queue中。
3. 消費(fèi)者訂閱消息
在RabbitMQ中消費(fèi)者有2種方式獲取隊(duì)列中的消息:
a)??一種是通過basic.consume命令怎憋,訂閱某一個(gè)隊(duì)列中的消息,channel會自動在處理完上一條消息之后又碌,接收下一條消息。(同一個(gè)channel消息處理是串行的)绊袋。除非關(guān)閉channel或者取消訂閱毕匀,否則客戶端將會一直接收隊(duì)列的消息。
b)??另外一種方式是通過basic.get命令主動獲取隊(duì)列中的消息癌别,但是絕對不可以通過循環(huán)調(diào)用basic.get來代替basic.consume皂岔,這是因?yàn)閎asic.get RabbitMQ在實(shí)際執(zhí)行的時(shí)候,是首先consume某一個(gè)隊(duì)列展姐,然后檢索第一條消息躁垛,然后再取消訂閱。如果是高吞吐率的消費(fèi)者圾笨,最好還是建議使用basic.consume缤苫。
如果有多個(gè)消費(fèi)者同時(shí)訂閱同一個(gè)隊(duì)列的話,RabbitMQ是采用循環(huán)的方式分發(fā)消息的墅拭,每一條消息只能被一個(gè)訂閱者接收。例如涣狗,有隊(duì)列Queue谍婉,其中ClientA和ClientB都Consume了該隊(duì)列舒憾,MessageA到達(dá)隊(duì)列后,被分派到ClientA穗熬,ClientA回復(fù)服務(wù)器收到響應(yīng)镀迂,服務(wù)器刪除MessageA;再有一條消息MessageB抵達(dá)隊(duì)列唤蔗,服務(wù)器根據(jù)“循環(huán)推送”原則探遵,將消息會發(fā)給ClientB,然后收到ClientB的確認(rèn)后妓柜,刪除MessageB箱季;等到再下一條消息時(shí),服務(wù)器會再將消息發(fā)送給ClientA棍掐。
這里我們可以看出藏雏,消費(fèi)者再接到消息以后,都需要給服務(wù)器發(fā)送一條確認(rèn)命令作煌,這個(gè)即可以在handleDelivery里顯示的調(diào)用basic.ack實(shí)現(xiàn)掘殴,也可以在Consume某個(gè)隊(duì)列的時(shí)候,設(shè)置autoACK屬性為true實(shí)現(xiàn)粟誓。這個(gè)ACK僅僅是通知服務(wù)器可以安全的刪除該消息奏寨,而不是通知生產(chǎn)者,與RPC不同鹰服。?如果消費(fèi)者在接到消息以后還沒來得及返回ACK就斷開了連接病瞳,消息服務(wù)器會重傳該消息給下一個(gè)訂閱者,如果沒有訂閱者就會存儲該消息获诈。
既然RabbitMQ提供了ACK某一個(gè)消息的命令仍源,當(dāng)然也提供了Reject某一個(gè)消息的命令。當(dāng)客戶端發(fā)生錯(cuò)誤舔涎,調(diào)用basic.reject命令拒絕某一個(gè)消息時(shí)笼踩,可以設(shè)置一個(gè)requeue的屬性,如果為true亡嫌,則消息服務(wù)器會重傳該消息給下一個(gè)訂閱者嚎于;如果為false,則會直接刪除該消息挟冠。當(dāng)然于购,也可以通過ack,讓消息服務(wù)器直接刪除該消息并且不會重傳知染。
4.?持久化:
Rabbit MQ默認(rèn)是不持久隊(duì)列肋僧、Exchange、Binding以及隊(duì)列中的消息的,這意味著一旦消息服務(wù)器重啟嫌吠,所有已聲明的隊(duì)列止潘,Exchange,Binding以及隊(duì)列中的消息都會丟失辫诅。通過設(shè)置Exchange和MessageQueue的durable屬性為true凭戴,可以使得隊(duì)列和Exchange持久化,但是這還不能使得隊(duì)列中的消息持久化炕矮,這需要生產(chǎn)者在發(fā)送消息的時(shí)候么夫,將delivery mode設(shè)置為2,只有這3個(gè)全部設(shè)置完成后肤视,才能保證服務(wù)器重啟不會對現(xiàn)有的隊(duì)列造成影響档痪。這里需要注意的是,只有durable為true的Exchange和durable為ture的Queues才能綁定钢颂,否則在綁定時(shí)钞它,RabbitMQ都會拋錯(cuò)的。持久化會對RabbitMQ的性能造成比較大的影響殊鞭,可能會下降10倍不止遭垛。
5.?事務(wù):
對事務(wù)的支持是AMQP協(xié)議的一個(gè)重要特性。假設(shè)當(dāng)生產(chǎn)者將一個(gè)持久化消息發(fā)送給服務(wù)器時(shí)操灿,因?yàn)閏onsume命令本身沒有任何Response返回锯仪,所以即使服務(wù)器崩潰,沒有持久化該消息趾盐,生產(chǎn)者也無法獲知該消息已經(jīng)丟失庶喜。如果此時(shí)使用事務(wù),即通過txSelect()開啟一個(gè)事務(wù)救鲤,然后發(fā)送消息給服務(wù)器久窟,然后通過txCommit()提交該事務(wù),即可以保證本缠,如果txCommit()提交了斥扛,則該消息一定會持久化,如果txCommit()還未提交即服務(wù)器崩潰丹锹,則該消息不會服務(wù)器就收稀颁。當(dāng)然Rabbit MQ也提供了txRollback()命令用于回滾某一個(gè)事務(wù)。
6.?Confirm機(jī)制:
使用事務(wù)固然可以保證只有提交的事務(wù)楣黍,才會被服務(wù)器執(zhí)行匾灶。但是這樣同時(shí)也將客戶端與消息服務(wù)器同步起來,這背離了消息隊(duì)列解耦的本質(zhì)租漂。Rabbit MQ提供了一個(gè)更加輕量級的機(jī)制來保證生產(chǎn)者可以感知服務(wù)器消息是否已被路由到正確的隊(duì)列中——Confirm阶女。如果設(shè)置channel為confirm狀態(tài)颊糜,則通過該channel發(fā)送的消息都會被分配一個(gè)唯一的ID,然后一旦該消息被正確的路由到匹配的隊(duì)列中后张肾,服務(wù)器會返回給生產(chǎn)者一個(gè)Confirm芭析,該Confirm包含該消息的ID,這樣生產(chǎn)者就會知道該消息已被正確分發(fā)吞瞪。對于持久化消息,只有該消息被持久化后驾孔,才會返回Confirm芍秆。Confirm機(jī)制的最大優(yōu)點(diǎn)在于異步,生產(chǎn)者在發(fā)送消息以后翠勉,即可繼續(xù)執(zhí)行其他任務(wù)妖啥。而服務(wù)器返回Confirm后,會觸發(fā)生產(chǎn)者的回調(diào)函數(shù)对碌,生產(chǎn)者在回調(diào)函數(shù)中處理Confirm信息荆虱。如果消息服務(wù)器發(fā)生異常,導(dǎo)致該消息丟失朽们,會返回給生產(chǎn)者一個(gè)nack怀读,表示消息已經(jīng)丟失,這樣生產(chǎn)者就可以通過重發(fā)消息骑脱,保證消息不丟失菜枷。Confirm機(jī)制在性能上要比事務(wù)優(yōu)越很多。但是Confirm機(jī)制叁丧,無法進(jìn)行回滾啤誊,就是一旦服務(wù)器崩潰,生產(chǎn)者無法得到Confirm信息拥娄,生產(chǎn)者其實(shí)本身也不知道該消息吃否已經(jīng)被持久化蚊锹,只有繼續(xù)重發(fā)來保證消息不丟失,但是如果原先已經(jīng)持久化的消息稚瘾,并不會被回滾牡昆,這樣隊(duì)列中就會存在兩條相同的消息,系統(tǒng)需要支持去重孟抗。
消息持久化是?RabbitMQ?最為人津津樂道的特性之一迁杨,?RabbitMQ?能夠在付出最小的性能代價(jià)的基礎(chǔ)上實(shí)現(xiàn)消息的持久化,最大的奧秘就在于?RabbitMQ?多層消息隊(duì)列的設(shè)計(jì)上凄硼。下面铅协,本文就從?MessageQueue?的設(shè)計(jì)和消息在?MessageQueue?的生命周期兩個(gè)方面全面介紹??RabbitMQ?的消息隊(duì)列。
RabbitMQ完全實(shí)現(xiàn)了AMQP協(xié)議摊沉,類似于一個(gè)郵箱服務(wù)狐史。Exchange負(fù)責(zé)根據(jù)ExchangeType和RoutingKey將消息投遞到對應(yīng)的消息隊(duì)列中,消息隊(duì)列負(fù)責(zé)在消費(fèi)者獲取消息前暫存消息。在RabbitMQ中骏全,MessageQueue主要由兩部分組成苍柏,一個(gè)為AMQQueue,主要負(fù)責(zé)實(shí)現(xiàn)AMQP協(xié)議的邏輯功能姜贡。另外一個(gè)是用來存儲消息的BackingQueue试吁,本文重點(diǎn)關(guān)注的是BackingQueue的設(shè)計(jì)。
在RabbitMQ中BackingQueue又由5個(gè)子隊(duì)列組成:Q1楼咳、Q2熄捍、Delta、Q3和Q4母怜。RabbitMQ中的消息一旦進(jìn)入隊(duì)列余耽,不是固定不變的,它會隨著系統(tǒng)的負(fù)載在隊(duì)列中不斷流動苹熏,消息的狀態(tài)不斷發(fā)生變化碟贾。RabbitMQ中的消息一共有5種狀態(tài):
a)Alpha:消息的內(nèi)容和消息索引都保存在內(nèi)存中;
b)Beta:消息內(nèi)容保存在磁盤上轨域,消息索引保存在內(nèi)存中袱耽;
c)Gamma:消息內(nèi)容保存在磁盤上,消息索引在磁盤和內(nèi)存都有疙挺;
d)Delta:消息內(nèi)容和索引都在磁盤上扛邑;
注意:對于持久化的消息,消息內(nèi)容和消息索引都必須先保存到磁盤上铐然,才會處于上述狀態(tài)中的一種蔬崩,而Gamma狀態(tài)的消息只有持久化的消息才會有該狀態(tài)。
BackingQueue?中的?5?個(gè)子隊(duì)列中的消息狀態(tài)搀暑,?Q1?和?Q4?對應(yīng)的是?Alpha?狀態(tài)沥阳,?Q2?和?Q3?是?Beta?狀態(tài),?Delta?對應(yīng)的是?Delta?狀態(tài)自点。上述就是?RabbitMQ?的多層隊(duì)列結(jié)構(gòu)的設(shè)計(jì)桐罕,我們可以看出從?Q1?到?Q4?,基本經(jīng)歷的是由?RAM?到?DISK桂敛,再到?RAM?的設(shè)計(jì)功炮。這樣的設(shè)計(jì)的好處就是當(dāng)隊(duì)列負(fù)載很高的情況下,能夠通過將一部分消息由磁盤保存來節(jié)省內(nèi)存空間术唬,當(dāng)負(fù)載降低的時(shí)候薪伏,這部分消息又漸漸回到內(nèi)存,被消費(fèi)者獲取粗仓,使得整個(gè)隊(duì)列有很好的彈性嫁怀。下面我們就來看一下设捐,整個(gè)消息隊(duì)列的工作流程。
引起消息流動主要有兩方面的因素:其一是消費(fèi)者獲取消息塘淑;其二是由于內(nèi)存不足萝招,引起消息的換出到磁盤上(?Q1-.>Q2?、?Q2->Delta?存捺、?Q3->Delta?槐沼、?Q4->Q3?)。?RabbitMQ?在系統(tǒng)運(yùn)行時(shí)會根據(jù)消息傳輸?shù)乃俣扔?jì)算一個(gè)當(dāng)前內(nèi)存中能夠保存的最大消息數(shù)量(?Target_RAM_Count?)召噩,當(dāng)內(nèi)存中的消息數(shù)量大于該值時(shí)母赵,就會引起消息的流動。進(jìn)入隊(duì)列的消息找筝,一般會按著?Q1->Q2->Delta->Q3->Q4?的順序進(jìn)行流動赛糟,但是并不是每條消息都一定會經(jīng)歷所有的狀態(tài),這個(gè)取決于當(dāng)時(shí)系統(tǒng)的負(fù)載狀況。
當(dāng)消費(fèi)者獲取消息時(shí)买乃,首先會從?Q4?隊(duì)列中獲取消息,如果?Q4?獲取成功枢泰,則返回笨蚁,如果?Q4?為空,則嘗試從?Q3?獲取消息瓷胧;首先显拳,系統(tǒng)會判斷?Q3?隊(duì)列是否為空,如果為空搓萧,則直接返回隊(duì)列為空杂数,即此時(shí)隊(duì)列中無消息(后續(xù)會論證)。如果不為空瘸洛,則取出?Q3?的消息揍移,然后判斷此時(shí)?Q3?和?Delta?隊(duì)列的長度,如果都為空反肋,則可認(rèn)為?Q2?那伐、?Delta?、?Q3?和?Q4?全部為空?(后續(xù)說明?)?石蔗,此時(shí)將?Q1?中消息直接轉(zhuǎn)移到?Q4?中罕邀,下次直接從?Q4?中獲取消息。如果?Q3?為空养距,?Delta?不空诉探,則將?Delta?中的消息轉(zhuǎn)移到?Q3?中;如果?Q3?非空铃在,則直接下次從?Q3?中獲取消息阵具。在將?Delta?轉(zhuǎn)移到?Q3?的過程中碍遍,?RabbitMQ?是按照索引分段讀取的,首先讀取某一段阳液,直到讀到的消息非空為止怕敬,然后判斷讀取的消息個(gè)數(shù)與?Delta?中的消息個(gè)數(shù)是否相等,如果相等帘皿,則斷定此時(shí)?Delta?中已無消息东跪,則直接將?Q2?和剛讀到的消息一并放入?Q3?中。如果不相等鹰溜,則僅將此次讀到的消息轉(zhuǎn)移到Q3?中虽填。這就是消費(fèi)者引起的消息流動過程。
下面我們分析一下由于內(nèi)存不足引起的消息換出曹动。消息換出的條件是內(nèi)存中保存的消息數(shù)量?+?等待?ACK?的消息的數(shù)量?>Target_RAM_Count?斋日。當(dāng)條件觸發(fā)時(shí),系統(tǒng)首先會判斷如果當(dāng)前進(jìn)入等待?ACK?的消息的速度大于進(jìn)入隊(duì)列的消息的速度時(shí)墓陈,會先處理等待?ACK?的消息恶守。步驟基本上?Q1->Q2?或者?Q3?移動,取決于?Delta?隊(duì)列是否為空贡必。?Q4->Q3?移動兔港,?Q2?和Q3?向?Delta?移動。
最后仔拟,我們來分析一下前面遺留的兩個(gè)問題衫樊,一個(gè)是為什么?Q3?隊(duì)列為空即可認(rèn)定整個(gè)隊(duì)列為空。試想如果?Q3?為空利花,Delta?不空科侈,則在?Q3?取出最后一條消息時(shí),?Delta?上的消息就會被轉(zhuǎn)移到?Q3?上晋被,與?Q3?空矛盾兑徘。如果?Q2?不空,則在?Q3?取出最后一條消息羡洛,如果?Delta?為空時(shí)挂脑,會將?Q2?的消息并入?Q3?,與?Q3?為空矛盾欲侮。如果?Q1?不空崭闲,則在?Q3?取出最后一條消息,如果?Delta?和?Q3?均為空時(shí)威蕉,則將?Q1?的消息轉(zhuǎn)移到?Q4?中刁俭,與?Q4?為空矛盾。這也解釋了另外一個(gè)問題韧涨,即為什么?Q3?和Delta?為空牍戚,?Q2?就為空侮繁。
上述就是整個(gè)消息在?RabbitMQ?隊(duì)列中流動過程。從上述流程可以看出如孝,消息如果能夠被盡早消費(fèi)掉宪哩,就不需要經(jīng)歷持久化的過程,因?yàn)檫@樣會加系統(tǒng)的開銷第晰。如果消息被消費(fèi)的速度過慢锁孟,?RabbitMQ?通過換出內(nèi)存的方式,防止內(nèi)存溢出茁瘦。
作者:Cat Qi
出處:http://qixuejia.cnblogs.com/
本文版權(quán)歸作者和博客園共有品抽,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明甜熔,且在文章頁面明顯位置給出原文連接圆恤,否則保留追究法律責(zé)任的權(quán)利