03.理解RabbitMQ消息通信中的基本概念

當提到消息通信時,我們腦海裏最先浮現的可能是郵箱和即時通信(IM),不過這些模型並非我們討論的RabbitMQ消息通信。比如說,雖然AMQP(高級消息隊列協議)像郵箱那樣爲離線消費者存儲消息,但是這些根據標籤路由的消息更爲靈活。同時和郵件不同的是,這些消息沒有固定的結構,甚至可以直接存儲二進制數據。同時也不同於即時通信(IM)協議,AMQP隱去了消息的發送方和接收方。AMQP消息能以一對多的廣播方式進行路由,也可以選擇以一對一的方式路由。在IM中,你只能一對一通信。

由於AMQP消息通信與其他通信協議不同,因此接下來我們將要解釋AMQP中的術語和構造。

首先我們看下什麼是消費者和生產者。

消費者和生產者

RabbitMQ在應用程序和服務器之間扮演着路由器的角色。所以當應用程序連接到RabbitMQ時,他就必須決定:我是在發送還是在接收呢?或者從AMQP的角度思考,我是一個生產者還是一個消費者呢?

生產者(producer)創建消息,然後發佈(發送)到代理服務器(RabbitMQ)。

那麼,消息又是什麼呢?

消息包括兩部分:有效載荷(payload)和標籤(label)。有效載荷就是你想要傳輸的數據。它可以是任何內容,一個JSON數組或者是你喜歡的高清無碼動作片。RabbitMQ不會在意這些。那麼,標籤又是做什麼用的呢?它描述了有效載荷,並且RabbitMQ用它來決定誰將獲得消息的拷貝。

消費者連接到代理服務器,並訂閱到隊列(queue)上。如果把消息隊列想象成一個郵箱。每當消息達到特定的郵箱時,RabbitMQ會將其發送給其中一個訂閱或監聽的消費者那裏,當消費者接收到消息時,它只得到消息的一部分:有效載荷。在消息路由過程中,消息的標籤並沒有隨有效載荷一同傳遞。RabbitMQ甚至不會告訴你是誰生產/發送了消息。如果想明確知道是誰生產了此消息的話,就要看生產者是否把發送方消息放入到有效載荷中。

下圖描述的是生產者到消費者的消息流

其實,整個過程很簡單。生產者創建消息,消費者接受這些消息。你的應用程序可以作爲生產者,向其他應用程序發送消息。或者作爲一個消費者,接受消息。也可以同時是消費者也是生產者。不過,在這之前,它必須先建立一條信道(channel)。

那麼,什麼是信道呢?

信道是建立在“真實的”TCP連接內的虛擬連接。當你連接到Rabbit時,你的應用程序和Rabbit代理服務器之間就會創建一條TCP連接。一旦TCP連接打開(你通過了認證),應用程序就會創建一條AMQP信道。不論是發佈消息、訂閱隊列還是接收消息,這些動作都是通過信道完成的。

那我們爲什麼不直接通過TCP連接發送AMQP命令呢?

主要原因在於對操作系統來說建立和銷燬TCP會話是非常昂貴的開銷。假設我們從隊列中消費消息,並根據服務需求合理調度線程。如果你只進行TCP連接,即每個線程都要自行連接到Rabbit。也就是說高峯期每秒都會有成千上萬條連接。這不僅造成了TCP連接的巨大浪費,而且操作系統每秒也就只能建立這麼點數量的連接。因此,你可能會很快就遇到性能瓶頸了。

然而,如果我們爲所有線程只使用一條TCP連接以滿足性能方面的要求,又能保證每個線程的私密性,就像擁有了獨立連接一樣,豈不很完美。這就是要引入信道概念的原因。

線程啓動後,會在現成的連接上創建一條信道,也就獲得了連接到Rabbit上的私密通信路徑,而不會給操作系統的TCP棧造成額外負擔。在一條TCP連接上創建多少條信道是沒有限制的。你也可以把它想象成一束光纖電纜。TCP連接就像電纜,而AMQP信道就像一條條獨立光纖束。

隊列

從概念上來講,AMQP消息路由必須有三部分:交換機、隊列和綁定。生產者把消息發佈到交換機上;消息最終達到隊列,並被消費者接收;綁定決定了消息如何從路由器路由到特定的隊列。我們在研究交換機和綁定之前,需要先理解隊列的概念和工作原理。

隊列就如同具名郵箱。消息最終到達隊列中並等待消費。那麼消費者是如何從特定的隊列中接收消息的呢?

消費者主要通過兩種方式從特定的隊列中接收消息。

(1)通過AMQP的basic.consume命令訂閱。此時會將信道設置爲接收模式,直到取消隊列的訂閱爲止。訂閱了消息後,消費者在消費或拒絕了最近接收的那條消息後,就能從隊列中自動的接收下一條消息。
(2)通過AMQP的basic.get命令請求單條消息。如果要獲得更多消息的話,需要再次發送basic.get命令。切記,不要將basic.get放在一個循環裏來替代basic.consume。因爲這會嚴重影響Rabbit的性能。你可以大致理解爲,basic.get命令會訂閱消息,獲取單條消息後,然後取消訂閱。消費者理應始終使用basic.consume來實現高吞吐量。

當有消費者訂閱了隊列,如果有消息的話,消息會立即發送給這些訂閱的消費者。如果消息到達了無人訂閱的隊列上,消息將會在隊列中等待,直到有消費者訂閱該隊列。

那麼,當有多個消費者訂閱到同一個隊列上時,消息又是如何分發的呢?

當Rabbit隊列擁有多個消費者時,隊列收到的消息將以循環的方式發送給消費者。每條消息只會發送給一個訂閱的消費者。當消費者確認接收到了消息後,Rabbit將會把消息從隊列中刪除。

你可能注意到了,剛纔提到了對消息進行確認。是的,消費者接收到的每一條消息都必須進行確認。消費者必須通過AMQP的basic.ack命令顯式地向RabbitMQ發送一個確認,或者在訂閱到隊列的時候就將auto_ack參數設置爲true。當設置了auto_ack時,一旦消費者接收消息,RabbitMQ會自動視其確認了消息。

如果消費者收到一條消息,然後確認之前從Rabbit斷開連接/從隊列上取消訂閱,RabbitMQ會認爲這條消息沒有分發,然後重新分發給下一個訂閱的消費者。如果你的應用程序崩潰了,這樣做可以確保消息會被髮送給另一個消費者進行處理。

另一方面,如果應用程序有bug而忘記確認消息啦,Rabbit將不會給該消費者發送更多的消息。這是因爲在上一條消息被確認之前,Rabbit會認爲這個消費者並沒有準備好接收下一條消息。

在收到消息後,如果你想要明確拒絕而不是確認收到該消息的話,該如何做呢?比如說,你在處理消息的時候遇到了不可恢復的錯誤,或者是格式錯誤的消息等。

只要消息尚未確認,你則有以下兩種選擇:
(1)把消費者從Rabbit服務器斷開連接,這會導致Rabbit自動重新把消息入隊,並且發給另一個消費者。這樣連接/斷開的方式會額外增加Rabbit的負擔,如果所有消費者處理消息時都會遇到錯誤的話,會導致潛在的重大負荷。
(2)RabbitMQ 2.0.0以及更新的版本,那就可以使用AMQP的basic.reject命令。顧名思義,basic.reject允許消費者拒絕RabbitMQ發送的消息。如果把reject命令的requeue參數設置爲true的話,RabbitMQ會將消息重新發送給下一個訂閱的消費者。如果設置爲false的話,Rabbit MQ會把消息從隊列中移除,而不會把它發送給新的消費者。如果你檢測到一條格式錯誤的消息而任何一個消費者都無法處理的時候,這樣做就十分有用。

還有一件更重要的事情,如何創建隊列。消費者和生產者都能使用queue.declare命令來創建隊列。當創建隊列時,你常常想要指定隊列名稱。消費者訂閱隊列時需要隊列名稱,並在創建綁定時也需要指定隊列名稱。如果不指定隊列名稱的話,Rabbit會分配一個隨機名稱並在queue.declare命令的響應中返回(對於構建AMQP上的RPC應用來說,使用臨時“匿名”隊列很有用)。

在隊列設置中,有一些有用的參數,比如:

  • exclusive 如果設置爲true的話,隊列將變成私有的,此時只有你的應用程序才能消費隊列消息。當你想要限制一個隊列只有一個消費者的時候很有幫助。
  • auto-delete 當最後一個消費者取消訂閱的時候,隊列就會自動移除。如果你需要臨時隊列只爲一個消費者服務的話,請結合auto-deleteexclusive。當消費者斷開連接時,隊列就被移除了。

如果你嘗試聲明一個已經存在的隊列會發生什麼呢?只要聲明參數完全匹配現存的話,Rabbit就什麼都不做,併成功返回,就好像這個隊列已經創建成功一樣,如果參數不匹配的話,隊列聲明嘗試會失敗。如果你只是想檢測隊列是否存在,則可以設置queue.declarepassive選項爲true。在該設置下,如果隊列存在,那麼queue.declare命令會成功返回;如果隊列不存在的話,queue.declare命令不會創建隊列而會返回一個錯誤。

當設計應用程序時,是該由生產者還是消費者來創建所需的隊列呢?看起來最自然的答案是由消費者來創建隊列。畢竟,消費者才需要訂閱隊列,而且總不能訂閱一個不存在的隊列,是吧?但是,先別這麼快下結論。

你首先選喲想清楚消息的生產者能否承擔得起丟失消息。發送出去的消息如果路由到了不存在的隊列上的話,Rabbit會忽略他們。因此,如果你不能承擔得起消息進入“黑洞”而丟失的話,你的生產者和消費者就都應該嘗試去創建隊列。另一方面,如果你能承擔得起丟失消息,或者你實現了一種方法來重新發布未處理的消息的話,你可以只讓自己的消費者來聲明隊列。

總的來說,隊列是AMQP消息通信的基礎模塊:

  • 爲消息提供了住所,消息在此等待消費。
  • 對負載均衡來說,隊列是絕佳方案。只需附加一堆消費者,並讓RabbitMQ以循環的方式均勻地分配發來的消息。
  • 隊列是Rabbit中消息的最後的終點,除非消息進入了“黑洞”。

我們瞭解了隊列之後,那麼消息又是如何到達隊列的呢?接下來,讓我們認識一下AMQP的交換機和綁定吧。

交換機和綁定

當你想要將消息投遞到隊列時,你通過把消息發送給交換機來完成。然後,根據確定的規則,Rabbit MQ 將會決定消息該投遞到哪個隊列。這些規則被稱爲路由鍵。隊列通過路由鍵綁定到交換機。當你把消息發送到代理服務器時,消息將擁有一個路由鍵–即便是空的–RabbitMQ也會將其和綁定使用的路由鍵進行匹配。如果相匹配的話,那麼消息將會投遞到該隊列。如果路由的消息不匹配任何綁定模式的話,消息將進入“黑洞”。

在AMQP中你還可以直接將隊列綁定到交換機上,而不使用路由鍵,然後你發送給交換機的每一條沒有路由鍵的消息,都會投遞到上述隊列中去。

服務器會根據路由鍵將消息從交換機路由到隊列,但是它是如何處理投遞到多個隊列的情況的呢?

協議中定義的不同類型交換機發揮了作用。以供四種類型:direct、fanout、topic 和 headers。每一種類型實現了不同的路由算法。headers交換機允許你匹配AMQP消息的header而非路由鍵。除此之外,headers交換機和direct交換機完全一致,但性能會差很多。因此它並不太實用,而且幾乎再也用不到了。所以,接下來,我們看下除了headers交換機外的其他三種。

默認交換機(一種特殊的直連交換機)

在消息代理服務器實現direct類型交換機時,就已經包含了一個空白字符串名稱的默認交換機(default exchange)。它有一個特殊的屬性使得它對於簡單應用特別有用處:那就是每個新建隊列都會自動綁定到默認交換機上,綁定的路由鍵(routing key)名稱與隊列名稱相同。

舉個栗子:當你聲明瞭一個名爲"search-indexing-online"的隊列,AMQP代理會自動將其綁定到默認交換機上,綁定(binding)的路由鍵名稱也是爲"search-indexing-online"。因此,當攜帶着名爲"search-indexing-online"的路由鍵的消息被髮送到默認交換機的時候,此消息會被默認交換機路由至名爲"search-indexing-online"的隊列中。換句話說,默認交換機看起來貌似能夠直接將消息投遞給隊列,儘管技術上並沒有做相關的操作。

當默認的交換機無法滿足應用程序的需求時,你可以聲明你自己的直連交換機。

直連交換機

直連型交換機(direct exchange)是根據消息攜帶的路由鍵(routing key)將消息投遞給對應隊列的。直連交換機主要用來處理消息的單播路由(unicast routing)

  • 將一個隊列綁定到某個交換機上,同時賦予該綁定一個路由鍵(routing key)
  • 當一個攜帶着路由鍵爲RK的消息被髮送給直連交換機時,交換機會把它路由給綁定值同樣爲RK的隊列。

直連交換機經常用來循環分發任務給多個工作者(workers)。當這樣做的時候,我們需要明白一點,在AMQP 0-9-1中,消息的負載均衡是發生在消費者(consumer)之間的,而不是隊列(queue)之間。

接下來,我們看下主要處理消息的廣播路由的扇形交換機。

扇型交換機

扇型交換機(funout exchange)將消息路由給綁定到它身上的所有隊列,而不理會綁定的路由鍵。如果N個隊列綁定到某個扇型交換機上,當有消息發送給此扇型交換機時,交換機會將消息的拷貝分別發送給這所有的N個隊列。扇型用來交換機處理消息的廣播路由(broadcast routing)。

舉例來說,一個Web應用程序可能需要在用戶上傳新的圖片時,用戶相冊必須清除緩存,同時用戶應該得到些積分獎勵。你可以將兩個隊列綁定到圖片上傳交換機上。一個用於清除緩存,另一個用於增加用戶積分。從這個場景中你可以瞭解到.使用交換機、綁定和隊列比直接向指定的隊列發送消息要有優勢。假設應用程序的第一個需求是在圖片上傳到網站上後,需要清除用戶相冊緩存。你可以通過只使用一個隊列就能輕易完成。但是當產品負責人讓你實現一個新功能,即在上傳完成後給用戶一點獎勵,你該怎麼辦呢?如果你是直接將消息發送給隊列的話,就不得不修改發送方的代碼,以將消息發送給新的用戶積分(points)隊列。如果你使用的是fanout交換機的話,你唯一需要做的就是爲新的消費者寫一段代碼,然後聲明新的隊列並將其綁定到fanout交換機上。就如同我們之前講的那樣,發送方的代碼和消費者的代碼兩者之間完全解藕了,這允許你輕而易
舉地添加應用程序的功能。

最後,我們來看下主要處理消息的多播路由的主題(topic)交換機

主題交換機

主題交換機(topic exchanges)通過對消息的路由鍵和隊列到交換機的綁定模式之間的匹配,將消息路由給一個或多個隊列。主題交換機經常用來實現各種分發/訂閱模式及其變種。主題交換機通常用來實現消息的多播路由(multicast routing)。

主題交換機擁有非常廣泛的用戶案例。無論何時,當一個問題涉及到那些想要有針對性的選擇需要接收消息的 多消費者/多應用(multiple consumers/applications) 的時候,主題交換機都可以被列入考慮範圍。

下面我們用Web應用程序日誌系統爲例。

其中,有兩個生產者發佈消息,分別綁定topic交換機,路由鍵分別爲log.criticalalert.critical
下面有三個消費者分別聲明瞭三個隊列。分別是:

  • 綁定topic交換機,路由鍵前綴爲log.開頭
  • 綁定topic交換機,路由鍵後綴爲.critical結束
  • 綁定topic交換機,路由鍵前綴爲alert.開頭

從上面可以看到,單個.把路由鍵分爲了幾部分,*匹配特定位置的任意文本。如果要匹配所有規則,你可以使用#

我們在理解了交換機、綁定和隊列之後,你可能會認爲自己已經掌握了RabbitMQ的所有特性。但是,隨着深入使用,你會發現有一個概念我們尚未討論:虛擬主機:vhost

虛擬主機和隔離

每一個RabbitMQ服務器都能創建虛擬消息服務器,我們稱之爲虛擬主機(vhost)。每一個vhost本質上都是一個mini版的RabbitMQ服務器,擁有自己的隊列、交換機和隊列。更重要的是,它擁有自己的權限機制。這使得你能夠安全地使用一個RabbitMQ服務器來服務衆多的應用程序。

vhost之於Rabbit就像虛擬機之於物理服務器一樣:它們通過在各個實例間提供邏輯上的分離,允許你爲不同應用程序安全保密地運行數據。這很有用.它既能將同一Rabbit的衆多客戶區分開來,又可以避免隊列和交換機的命名衝突。否則你可能不得不運行多個Rabbit,並忍受隨之而來頭疼的管理問題。相反.你可以只運行一個Rabbit,然後按需啓動或關閉vhost。

vhost是AMQP概念的基礎,你必須在連接時進行指定。由於RabbitMQ包含了開箱即用的默認vhost:"/",因此使用起來非常簡單。如果你不需要多個vhost的話,那麼就使用默認的吧。通過使用缺省的guest用戶名和密碼guest就可以訪問默認vhost。爲安全起見,你應該更改它。AMQP的一個有趣的地方在於它並沒有指定權限控制是在vhost級別還是在服務器端級別實現。這留給了服務器的開發者去決定。在RabbitMQ的例子中,權限控制是以vhost爲單位的。

當你在Rabbit裏創建一個用戶時,用戶通常會被指派給至少一個vhost,並且只能訪問被指派vhost內的隊列、交換機和綁定。當你在設計消息通信架構時,記住vhost之間是絕對隔離的。你無法將vhost banana tree上的交換機綁定到vhostoak tre。中的隊列去。事實上,這既保證了安全性,又確保了可移植性。

我們可以看到vhost帶來的巨大益處,那麼如何創建它們呢?vhost和權限控制非常獨特,不同於隊列、交換機和綁定,它們是AMQP中唯一無法通過AMQP協議創建的基元。對於RabbitMQ來說,你需要通過RabbitMQ的安裝路徑下./sbin/目錄下的rabbitmqctl工具來創建。

  • rabbitmqctl add_vhost [vhost_name] 創建一個vhost
  • rabbitmqctl delete_vhost [vhost_name] 刪除一個vhost
  • rabbitmqctl list_vhost Rabbit服務器上運行着哪些vhost

但是,在平常工作中,我們經常會使用Web管理界面進行操作。

到目前爲止呢,通過vhost你保障了隊列和交換機的安全。現在我們來討論下當Rabbit崩潰或者重啓時,如何確保關鍵信息不丟失。

持久化和策略

默認情況下,重啓RabbitMQ服務器後,那些隊列和交換機就都消失了(隨同裏面的消息)。原因在於每個隊列和交換機的durable屬性。該屬性默認清況爲false,它決定了abbitMQ是否需要在崩潰或者重啓之後重新創建隊列(或者交換機)。將它設置爲true,這樣你就不需要在服務器斷電後重新創建隊列和交換機了。你也許會認爲把隊列和交換機的durable屬性設置爲true就足夠可以讓消息倖免於重啓.但是你錯了。隊列和交換機當然必須被設置成true,但光這樣做還不夠。

能從AMQP服務器崩潰中恢復的消息,我們稱之爲持久化消息、。在消息發佈前,通過把它的“投遞模式”( delivery mode)選項設置爲2 (AMQP客戶端可能會使用人性化的常量來代替數值)來把消息標記成持久化。到目前爲止,消息還只是被表示爲持久化的,但是它還必須被髮布到持久化的交換機中併到達持久化的隊列中才行。如果不是這樣的話,則包含持久化消息的隊列(或者交換機)會在Rabbit崩潰重啓後不復存在,從而導致消息成爲孤兒。因此,如果消息想要從Rabbit崩潰中恢復,那麼消息必須:

  • 把它的投遞模式選項設置爲2(持久)
  • 發送到持久化的交換機
  • 到達持久化的隊列

做到了以上三點,你就不用擔心你的關鍵信息無緣無故的失蹤啦。

那麼,RabbitMQ確保持久性消息又是怎麼從服務器重啓中恢復過來的呢?

是因爲他們把消息寫入磁盤上的一個持久化日誌文件中了。當發佈一條持久性消息到持久交換機上時,Rabbit會在消息提交到日誌文件後才發送響應。記住,之後這條消息如果路由到了非持久隊列的話,它會自動從持久性日誌中移除.並且無法從服務器重啓中恢復。如果你使用持久性消息的話.則確保之前提到的持久性消息的那三點都必須做到位(我們再怎麼強調也不爲過)。一旦你從持久化隊列中消費了一條持久性消息的話(並且確認了它).RabbitMQ會在持久化日誌中把這條消息標記爲等待垃圾收集。在你消費持久性消息前,如果RabbitMQ重啓的話,服務器會自動重建交換機和隊列(以及綁定),重播持久性日誌文件中的消息到合適的隊列或者交換機上(取決於Rabbit服務器宕機的時候,消息處在路由過程的哪個環節))。

你可能認爲自己應該爲所有的消息都啓用持久化消息通信。你可以這樣做,但同時你也要爲此付出代價:性能。寫人磁盤要比存入內存中慢不止一點點,而且會極大地減少RabbitMQ服務器每秒可處理的消息總數。使用持久化機制而導致消息吞吐量降低至少10倍的情況並不少見(將RabbitMQ的消息存儲置於SSD上的話,就可以極大地提升持久化消息通信的性能。)。另外還有一點就是,持久性消息在RabbitMQ內建集羣環境下工作得並不好。雖然RabbitMQ集羣允許你和集羣中的任何節點的任一隊列進行通信,但是事實上那些隊列均勻地分佈在各個節點而沒有冗餘(在集羣中任何一個隊列都沒有備份的拷貝)。如果運行seed bin隊列的集羣節點崩潰了.那麼直到節點恢復前,這個隊列也就從整個集羣中消失了(如果隊列是可持久化的)。更重要的是.當節點宕機時,其上的隊列也都不可用了,而且持久化隊列也無法重建。這就會導致消息丟失。我們會在下次分享的時候更詳細地討論這一情況,並給出替代的集羣方法來解決這個問題。

在我們剛開始討論MQ的時候,就已經說過了MQ有一個致命的缺點就是:上游無法知道下游的執行結果。由於發佈操作不返回任何消息給生產者,那你怎麼知道服務器是否已經持久化了持久消息到硬盤呢?服務器可能會在把消息寫入磁盤前就宕機了,消息因此丟失,而你卻不知道。 而這就是事務發揮作用的地方。

在AMQP中,在把信道設置成事務模式後。你通過信道發送那些想要確認的消息,之後還有多個其他AMQP命令。這些命令是執行還是忽略,取決於第一條消息發送是否成功。一旦你發送完所有命令,就可以提交事務了。如果事務中的首次發佈成功了,那麼信道會在事務中完成其他AMQP命令。如果發送失敗的話,其他AMQP命令將不會執行。

RabbitMQ中與事務機制有關的方法有三個,分別是Channel裏面的txSelect(),txCommit()以及txRollback()。

  • txSelect用於將當前Channel設置成transaction模式
  • txCommit用於提交事務
  • txRollback用於回滾事務

在通過txSelect開啓事務之後,我們便可以發佈消息給broker代理服務器了,如果txCommit提交成功了,則消息一定是到達broker了,如果在txCommit執行之前broker異常奔潰或者由於其他原因拋出異常,這個時候我們便可以捕獲異常通過txRollback回滾事務了。

雖然事務是正式AMQP 0-9-1規範的一部分,但是它卻有致命的缺陷:幾乎吸乾了Rabbit的性能。使用事務不但會降低大約2-10倍的消息吞吐量,而且會使生產者應用程序產生同步。而你使用消息通信就是想要避免同步。知曉了所有這一切之後,RabbitMQ團隊決定拿出更好的方案來保證消息投遞:發送方確認模式

和事務相仿,你需要告訴Rabbit將信道設置成爲confirm模式.而且你只能通過重新創建信道來關閉該設置。一旦信道進人confirm模式.所有在信道上發佈的消息都會被指派一個唯一的ID號(從1開始)。一旦消息被投遞給所有匹配的隊列後,信道會發送一個發送方確認模式給生產者應用程序(包含消息的唯一ID )。這使得生產者知曉消息已經安全到達目的隊列了。如果消息和隊列是可持久化的,那麼確認消息只會在隊列將消息寫入磁盤後纔會發出。發送方確認模式的最大好處是它們是異步的。一旦發佈了一條消息,生產者應用程序就可以在等待確認的同時繼續發送下一條。當確認消息最終收到的時候,生產者應用的回調方法就會被觸發來處理該確認消息。如果Rabbit發生了內部錯誤從而導致了消息的丟失,Rabbit會發送一條nack ( not acknowledged,未確認)消息。就像發送方確認消息那樣,只不過這次說明的是消息已經丟失了。同時.由於沒有消息回滾的概念(同事務相比),因此發送方確認模式更加輕量級,同時對Rabbit代理服務器的性能影響幾乎可以忽略不計。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章