【一】rabbitmq原理概述(exchange、queue、channel、connection、vhost)持久化、消息分發

一、架構圖

網上找的

二、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機制)

【二】rabbitmq生產者確保消息一定送達

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隊列的特點是:

  1. 只對首次聲明它的連接(Connection)可見,注意,是首次聲明它的connection不是channel。
  2. 會在其連接斷開的時候自動刪除。

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

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