一、架構圖
網上找的
二、exchange(交換機)
生產者發消息發往交換機,交換機會自己投遞消息到綁定的queue隊列
這裏有幾個點需要注意
1.exchange根據什麼規則把消息投遞到哪些queue中?
exchange有4種類型
direct:
對routing_key進行嚴格匹配,當消息來到的時候,只有exchange與某queue綁定的routing_key完全匹配纔將消息投遞到該queue
topic:
對routing_key進行通配符模糊匹配,滿足條件的queue都能收到消息,這裏的routing_key以"."分隔,*匹配一個單詞,#匹配多個單詞,如果同一個queue滿足多個條件不會被投遞多次
一下圖爲例,如果消息的routingkey是quick.orange.rabbit,那麼Q1 Q2都會收到這條消息。
如果消息的routingkey是quick.orange.fox,那麼Q1會收到這條消息
headers:
根據消息體內的headers屬性匹配,綁定的時候可以制定鍵值對。不依賴routing_key匹配。
沒有圖,大致邏輯與direct差不多,只不過不是用的routing_key來匹配
fanout:
轉發消息到所有綁定隊列,不依賴routing_key匹配
在不需要路由的時候,一般是使用的這個類型的exchange。
發佈訂閱:兩個queue綁定到同一個exchange上,那麼同一個消息被髮送到exchange後,exchange會把這個消息發給綁定的所有隊列,兩個消費者,一人消費一個隊列,這就在這兩個消費者之間達到了發佈訂閱的效果
競爭消費:兩個消費者消費同一個隊列,這就達到了這兩個消費者之間的競爭消費效果。注意,下圖沒有畫exchange,實際上在寫代碼的時候不顯示指定exchange的數據是發送到一個默認的exchange上的。
2.exchange持久化怎麼搞?
如果不設置持久化,broker掛了,再重啓,這個exchange就不存在了。
在客戶端聲明exchange的時候有個入參來控制是否持久化
Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, String type) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange,
String type,
boolean durable,
boolean autoDelete,
boolean internal,
Map<String, Object> arguments) throws IOException;
void exchangeDeclareNoWait(String exchange,
String type,
boolean durable,
boolean autoDelete,
boolean internal,
Map<String, Object> arguments) throws IOException;
Exchange.DeclareOk exchangeDeclarePassive(String name) throws IOException;
上圖中,durable即是是否持久化
而autoDelete則是,當沒有queue綁定的時候是否自動刪除這個exchange
3.生產者ACK機制?(事務或者confirm機制)
4.投遞方法(basicPublish)中的mandatory和immediate
mandatory
當mandatory標誌位設置爲true時,如果exchange根據自身類型和消息routeKey無法找到一個符合條件的queue,那麼會調用basic.return方法將消息返回給生產者(Basic.Return + Content-Header + Content-Body);
當mandatory設置爲false時,出現上述情形broker會直接將消息扔掉。
immediate
當immediate標誌位設置爲true時,如果exchange在將消息路由到queue(s)時發現對於的queue上沒有消費者,那麼這條消息不會放入隊列中。當與消息routeKey關聯的所有queue(一個或者多個)都沒有消費者時,該消息會通過basic.return方法返還給生產者。
換句話說,無法找到一個消費者時,消息返還給生產者
三、queue隊列
消息隊列,先進先出,有緩存消息的能力。
1.有3種類型的隊列:
1.可以設置成是持久化隊列,這樣消息會落盤,沒有消費的消息重啓後不會丟。
2.如果是臨時隊列,則沒有持久化,堆積的數據在rabbitmq重啓後會丟失。
3.如果設置爲自動隊列,當沒有消費者消費這個隊列的時候,隊列會自動刪除。
2.持久化隊列怎麼搞?
在channel聲明隊列的時候可以設置
public com.rabbitmq.client.impl.AMQImpl.Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments) throws IOException {
validateQueueNameLength(queue);
return (com.rabbitmq.client.impl.AMQImpl.Queue.DeclareOk)this.exnWrappingRpc((new com.rabbitmq.client.AMQP.Queue.Declare.Builder()).queue(queue).durable(durable).exclusive(exclusive).autoDelete(autoDelete).arguments(arguments).build()).getMethod();
}
1.durable是否持久化隊列。
2.exclusive是否爲獨佔(排他)隊列。
exclusive隊列的特點是:
- 只對首次聲明它的連接(Connection)可見,注意,是首次聲明它的connection不是channel。
- 會在其連接斷開的時候自動刪除。
3.autoDelete是否自動刪除,如果爲是,當沒有消費者消費這個隊列的時候,這個隊列會被自動刪除。
注意,這裏只是說持久化隊列,持久化隊列在rabbitmq重啓後依舊存在,如果需要未消費的消息在重啓後依舊存在,還需要持久化消息
3.是否支持消息回溯?
不支持。rabbitmq不像kafka那樣,並沒有偏移量。rabbitmq即使是持久化隊列+持久化消息,在被消費後該數據會被標記爲刪除,等待回收。
4.mirror queue鏡像模式 (高可用)
另外一篇將
5.消費者ACK機制
【三】rabbitmq消費者ACK機制message acknowledgment
6.持久化消息怎麼搞
如果只是持久化隊列,沒有持久化消息,那麼重啓後,隊列存在,消息不存在了。
持久化消息的設置在channel.basicPublish方法的入參中
void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body)
throws IOException;
void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body)
throws IOException;
入參中的 BasicProperties props用於設置消息是否持久化
public BasicProperties(
String contentType,//消息類型如:text/plain
String contentEncoding,//編碼
Map<String,Object> headers,
Integer deliveryMode,//消息是否持久化 1:nonpersistent 2:persistent
Integer priority,//優先級
String correlationId,
String replyTo,//反饋隊列
String expiration,//expiration到期時間
String messageId,
Date timestamp,
String type,
String userId,
String appId,
String clusterId)
deliveryMode=1表示不需要持久化消息,deliveryMode=2表示需要持久化消息
7.消息是怎麼分發給消費者的?
Round-robin dispatching 循環分發(默認)
若存在多個consumer,每個consumer的負載可能不同,有些處理的快有些處理的慢,RabbitMQ並不管這些,只是簡單的以round-robin的方式分配message,這可能造成某些consumer積壓很多任務處理不完,而一些consumer長期處於飢餓狀態
那麼,Rabbit是如何處理這種問題呢?
Fair dispatch 公平分發
通過basic.qos方法設置prefetch_count=1,這樣RabbitMQ就會使得每個Consumer在同一個時間點最多處理一個Message,換句話說,在接收到該Consumer的ack前,它不會將新的Message分發給它
channel.basic_qos(prefetch_count=1)
注意,這種方法可能會導致queue滿。當然,這種情況下你可能需要添加更多的Consumer,或者創建更多的virtualHost來細化你的設計。
四、channel信道
是建立在真實的TCP連接內的虛擬連接(是我們與RabbitMQ打交道的最重要的一個接口)。
僅僅創建了客戶端到Broker之間的連接後,客戶端還是不能發送消息的,需要爲每一個Connection創建Channel,AMQP協議規定只有通過Channel才能執行AMQP的命令。
AMQP的命令都是通過信道發送出去的(我們大部分的業務操作是在Channel這個接口中完成的,包括定義Queue、定義Exchange、綁定Queue與Exchange、發佈消息等。)。
每條信道都會被指派一個唯一ID。在客戶端的每個連接裏,可建立多個channel,每個channel代表一個會話任務,理論上無限制,減少TCP創建和銷燬的開銷,實現共用TCP的效果。
之所以需要Channel,是因爲TCP連接的建立和釋放都是十分昂貴的,如果一個客戶端每一個線程都需要與Broker交互,如果每一個線程都建立一個TCP連接,暫且不考慮TCP連接是否浪費,就算操作系統也無法承受每秒建立如此多的TCP連接。
注1:一個生產者或一個消費者與MQ服務器之間只有一條TCP連接
注2:RabbitMQ建議客戶端線程之間不要共用Channel,至少要保證共用Channel的線程發送消息必須是串行的,但是建議儘量共用Connection。
五、connection連接
是Publisher/Consumer和Broker之間的TCP連接。斷開連接的操作只會在Publisher/Consumer端進行,Broker不會斷開連接,除非出現網絡故障或者Broker服務出現問題,Broker服務宕了。
六、virtual host虛擬主機
每一個vhost本質上是一個mini-rabbitmq server,分別管理各自的exchange,和bindings。
一個Broker裏可以開有多個VirtualHost,它的作用是用作不同用戶的權限分離。
這個特性在做多租戶的時候較方便
1.vhost特性
1.RabbitMQ默認的vhost是“/”開箱即用;
2.多個vhost是隔離的,多個vhost無法通訊,並且不用擔心命名衝突(隊列和交換器和綁定),實現了多層分離;
3.創建用戶的時候必須指定vhost;
2.vhost操作
可以通過rabbitmqctl工具命令創建:
rabbitmqctl add_vhost[vhost_name]
刪除vhost:
rabbitmqctl delete_vhost[vhost_name]
查看所有的vhost:
rabbitmqctl list_vhosts