一、發佈確認邏輯
生產者將信道設置成 confirm 模式,一旦信道進入 confirm 模式,所有在該信道上面發佈的消息都將會被指派一個唯一的 ID (從 1 開始),一旦消息被投遞到所有匹配的隊列之後,broker 就會發送一個確認給生產者 (包含消息的唯一 ID),這就使得生產者知道消息已經正確到達目的隊列了
當消息最終得到確認之後,生產者應用便可以通過回調方法來處理該確認消息,如果 RabbitMQ 因爲自身內部錯誤導致消息丟失,就會發送一條 nack 消息, 生產者應用程序同樣可以在回調方法中處理該 nack 消息。
二、發佈發佈確認的策略
發佈確認默認是沒有開啓的
,如果要開啓,需要調用方法 confirmSelect
,每當你要想使用發佈確認,都需要在 channel 上調用該方法
1、單個發佈確認
這是一種簡單的確認方式,它是一種同步確認發佈的方式,也就是發佈一個消息之後只有它被確認發佈,後續的消息才能繼續發佈,waitForConfirmsOrDie(long) 這個方法只有在消息被確認的時候才返回,如果在指定時間範圍內這個消息沒有被確認那麼它將拋出異常。
這種確認方式有一個最大的缺點就是:發佈速度特別的慢,因爲如果沒有確認發佈的消息就會阻塞所有後續消息的發佈,這種方式最多提供每秒不超過數百條發佈消息的吞吐量。當然對於某些應用程序來說這可能已經足夠了。
//單個確認 public static void publishMessageSingle() throws Exception{ Channel channel = Util.getChannel(); //隊列的聲明 String queueName = UUID.randomUUID().toString(); channel.queueDeclare(queueName,true,false,false,null); //開啓發布確認 channel.confirmSelect(); //開始時間 long begin = System.currentTimeMillis(); //批量發消息 for (int i = 0; i < MESSAGE_COUNT; i++) { String message = i + ""; channel.basicPublish("",queueName,null,message.getBytes()); //單個消息就馬上發佈確認 boolean flag = channel.waitForConfirms(); if(flag){ System.out.println("消息發送成功"); } } //結束時間 long end = System.currentTimeMillis(); System.out.println("發佈"+MESSAGE_COUNT+"個單獨確認消息,耗時"+(end-begin)+"ms"); }
2、批量確認發佈
與單個等待確認消息相比,先發布一批消息然後一起確認可以極大地提高吞吐量,當然這種方式的缺點就是:當發生故障導致發佈出現問題時,不知道是哪個消息出現問題了,我們必須將整個批處理保存在內存中,以記錄重要的信息而後重新發布消息。當然這種方案仍然是同步的,也一樣阻塞消息的發佈。
//批量確認 public static void publishMessageBatch() throws Exception{ Channel channel = Util.getChannel(); //隊列的聲明 String queueName = UUID.randomUUID().toString(); channel.queueDeclare(queueName,true,false,false,null); //開啓發布確認 channel.confirmSelect(); //批量確認消息大小 int batchSize = 100; //開始時間 long begin = System.currentTimeMillis(); //批量發消息 批量發佈確認 for (int i = 0; i < MESSAGE_COUNT; i++) { String message = i + ""; channel.basicPublish("",queueName,null,message.getBytes()); //每達到100次,批量發佈確認一次 if(i%batchSize == 0){ //發佈確認 channel.waitForConfirms(); } } //結束時間 long end = System.currentTimeMillis(); System.out.println("發佈"+MESSAGE_COUNT+"個批量確認消息,耗時"+(end-begin)+"ms"); }
3、異步確認發佈
異步確認雖然編程邏輯比上兩個要複雜,但是性價比最高,無論是可靠性還是效率都沒得說,他是利用回調函數來達到消息可靠性傳遞的,這個中間件也是通過函數回調來保證是否投遞成功。
//異步發佈確認 public static void publishMessageAsync() throws Exception{ Channel channel = Util.getChannel(); //隊列的聲明 String queueName = UUID.randomUUID().toString(); channel.queueDeclare(queueName,true,false,false,null); //開啓發布確認 channel.confirmSelect(); //開始時間 long begin = System.currentTimeMillis(); /** * deliveryTag:消息的標記 * multiple:是否批量確認 */ //消息確認成功 回調函數 ConfirmCallback ackCallback = (deliveryTag,multiple)->{ System.out.println("確確認消息"+deliveryTag); }; //消息確認失敗 回調函數 ConfirmCallback nackCallback = (deliveryTag,multiple)->{ System.out.println("未確認消息"+deliveryTag); }; //準備消息監聽器 監聽那些消息成功,那些消息失敗 /** * 1.監聽成功方法 * 2.監聽失敗方法 */ channel.addConfirmListener(ackCallback,nackCallback);//異步通知 //批量發送消息 for (int i = 0; i < MESSAGE_COUNT; i++) { String message = i + ""; channel.basicPublish("",queueName,null,message.getBytes()); } //結束時間 long end = System.currentTimeMillis(); System.out.println("發佈"+MESSAGE_COUNT+"個異步發佈確認消息,耗時"+(end-begin)+"ms"); }