RabbitMQ進階知識

RabbitMQ進階知識


1、消費端的確認

 channel.basicConsume(QUEUE_NAME,false,consumer);

將autoAck設置爲false,當消費端收到消息之後,隊列不會立即刪除這條消息,只有當消費端進行手動確認後纔會進行刪除即調用:這樣可以允許一條消息的處理時間可以很長。

 channel.basicAck(envelope.getDeliveryTag(),true);

當消費者設置爲手動簽收,但在處理消息的過程中,連接斷開了,此時這些消息會發送給其它客戶端。

2、消費端的拒絕

當消費端接收到消息後,允許拒絕這條消息,並且可以設置被拒絕的消息是否重新進入隊列中。

void basicReject(long deliveryTag, boolean requeue) throws IOException;

若requeue設置爲false,則這條消息會被刪除,若爲true,則不會被刪除,且會再次發過來。

void basicNack(long deliveryTag, boolean multiple, boolean requeue)

multiple 設置爲true進行批量拒絕。

注:當requeue爲false的時候,這些消息會進入死信隊列。

3、消息何去何從 mandatory參數

void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body)

生產者發送消息的時候,設置mandatory爲true,當消息發送給交換機,但沒有合適的隊列接收,此時在生產者端註冊一個監聽器,可以接收到發送失敗的消息。

channel.addReturnListener(new ReturnListener() {
    @Override
    public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) {
        System.out.println("replyCode :" + replyCode);
        System.out.println("replyText :" + replyText);
        System.out.println("exchange :" + exchange);
        System.out.println("routingKey :" + routingKey);
        System.out.println("properties :" + properties);
        System.out.println("body :" + new String(body));
    }
});

當mandatory爲false的時候,無法路由的消息會被直接丟棄。

4、備份交換器

爲一個交換器,綁定一個備胎交換器,當發送到第一個交換器的消息因爲rountingKey原因無法路由的時候,就會發送到備份交換器,這條消息的rountingKey保持不變。消息被髮送到備份交換器和生產者將消息直接發送到備份交換器的本質是一樣的,當備份交換器和mandatory共存時,備份交換器優先級更高。

// 聲明備份交換器
channel.exchangeDeclare("beifen","fanout",true,true,false,null);
channel.queueDeclare("beifen",false,false,false,null);
channel.queueBind("beifen","beifen","abc");
Map<String,Object> map = new HashMap<>();
map.put("alternate-exchange","beifen");
// 綁定備份交換器
channel.exchangeDeclare(EXCHANGE_NAME, "direct", true, true,false,map);
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "first");

5、消息的過期時間TTL

可以爲消息指定一個過期時間,將消息發送到隊列中後,經過過期時間,還沒有被消費則這條消息就會成爲一條過期消息,有兩種處理方式:沒有死信隊列的話,直接丟棄,有死信隊列的話,該消息會進入死信隊列中。

AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
    .deliveryMode(2)
    .contentEncoding("UTF-8")
    .expiration("10000")  // ms ---> 10s
    .build();

channel.basicPublish(EXCHANGE_NAME,"ttl",properties,message.getBytes());

過期時間的單位爲ms。

也可以爲整個隊列設置過期時間,這樣隊列中的所有消息的過期時間都是一樣的,兩者共存時,哪個短哪個優先級高。

爲隊列設置過期時間,時間到了,消息會立即被抹去,但爲消息單獨設置過期時間,即使消息到了也不會被抹去,這個被抹去的時機是在消費者進行消費的時候進行判斷的,若過期抹除,未過期繼續消費。

Map<String,Object> arguments = new HashMap<>();
// 隊列設置過期時間,隊列中所有消息的過期時間
arguments.put("x-message-ttl",5000);
channel.queueDeclare(QUEUE_NAME,false,false,false,arguments);

TTL = 0 表示要麼可以直接投遞給消費者,要麼直接過期。

6、死信隊列

當消息成爲死信消息之後就會被髮送到死信交換器,進而被髮送到與死信交換器綁定的死信隊列中。

一個消息成爲死信消息有以下幾種情況:

  • 消息過期
  • 隊列達到最大長度
  • 消息被拒絕且不重新入隊
arguments.put("x-dead-letter-exchange", "dead_exchange");
// 死信
channel.queueDeclare("dead_queue", false, false, false, null);
channel.exchangeDeclare("dead_exchange", "fanout", false, false, null);
channel.queueBind("dead_queue","dead_exchange","ttl");
// 業務隊列與死信交換機相關聯在一起
channel.queueDeclare("work_queue", false, false, false, arguments);
channel.exchangeDeclare("work_exchage", BuiltinExchangeType.DIRECT, false, false, null);
channel.queueBind("work_queue", "work_exchage", "ttl");
// 爲該條消息設置過期時間

7、延遲隊列

延遲隊列中存儲的是延遲消息,延遲消息是指消息被髮送後,並不是立即被消費,而是等待特定時機,消費者纔拿到這個消息進行消費。

使用死信隊列+ TTL來實現延遲隊列的功能,消費者此時訂閱的是死信隊列。

8、持久化

RabbitMQ中可以設置交換器,隊列、消息的持久化。

消息自身的持久化需要依賴隊列,因爲消息在隊列中存着,唯有消息和隊列都設置持久化,RabbitMQ重啓後,消息纔不會丟失。

消息的持久化會消耗性能,需要在可靠性和吞吐量之間做一個權衡。

9、事務機制

當消息發送出去後,還沒有到達隊列,Broker就宕機了此時即使有持久化機制,這條消息都會丟失,爲了解決這個問題,引入事務機制,發送之前開啓事務,然後發送消息,發送之後提交事務,如果發生異常可以進行回滾。事務機制非常消耗RabbitMQ的性能,因此又提出了發送方確認機制。爲什麼會慢呢?因爲事務是同步機制,消息發送過後,唯有等到消息被確認纔可以繼續發送下一條。

10,發送端確認機制

基於事務的性能問題,RabbitMQ團隊爲我們拿出了更好的方案,即採用**發送方確認模式,**該模式比事務更輕量,性能影響幾乎可以忽略不計。

原理:生產者將信道設置成confirm模式,一旦信道進入confirm模式,所有在該信道上面發佈的消息都會被指派一個唯一的id開始從1 開始。

可路由的消息,要等到消息被投遞到對應的隊列後,broker會發送一個確認給生產者,這就使得生產者知道消息已經發送到正確的目的隊列了。如果消息和隊列是可持久化的,那麼確認消息會在將消息寫入磁盤之後發出,broker回傳給生產者的確認消息中delivery-tag域包含了確認消息的序列號。

如果RabbitMQ因爲自身內部錯誤導致消息丟失【消息發送不到交換器】,就會發送一條nack消息,生產者應用程序同樣可以在回調方法中處理該nack消息決定下一步的處理。

確認機制一般和mandatory搭配使用,前者指的是發送不到交換機通知失敗,後者指的是發送不到隊列通知失敗。

11、消息分發

當RabbitMQ隊列有多個消費者的時候,默認採用輪詢的方式進行消息分發,但由於機器性能及消息本身等原因,可能導致某些消費者空閒,某些消費者負載過重。纔是需要使用到channel的basicQos方法進行限制。設置一個上限,當發送的消息達到上限後,不會再發送消息給這個消費者,唯有當消費者對之前的消息進行確認之後,纔會接着發送。

channel.basicQos(0,2,false);
com.rabbitmq.client.Consumer consumer = new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope,
                               AMQP.BasicProperties properties, byte[] body) {
        String msg = new String(body, "UTF-8");
        System.out.println("Received message : '" + msg + "'");
        channel.basicAck(envelope.getDeliveryTag(),false);
    }
};
channel.basicConsume(QUEUE_NAME,false,consumer);

12、消息的順序性

RabbitMQ不能保證消息的順序消費。哪些情況會導致消息亂序呢??

  • 事務機制,重發導致亂序
  • confirm機制,重發導致亂序
  • 消息的優先級
  • 不同過期時間的消息
  • 消費端設置requeue

可以爲消息設置一個序列號來解決這個問題,和業務強相關。

13、消息傳輸保障

  • 消息成功發到隊列
  • 消費端成功消費

爲了保證消息的可靠性傳輸,發送端,開啓失敗通知Confirm,mandatory這些參數,開啓消息的持久化機制,消費端,開啓自動確認機制。這樣雖然極大概率保證消息的可靠性傳輸,但會引起消息的重傳問題,進而導致重複消費,此時需要考慮消費端的冪等性。

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