rabbit - producer的confirm和consumer的ack模式

本篇和大家分享的是關於rabbit的生產和消費方的一些實用的操作;正如文章標題,主要內容如producer的confirm和consumer的ack,這兩者使用的模式都是用來保證數據完整性,防止數據丟失。

  • producer的confirm模式
  • consumer的ack模式

producer的confirm模式

首先,有這樣一種業務場景1:a系統在做活動前,需要給用戶的手機發送一條活動內容短信希望用戶來參加,因爲用戶量有點大,所以通過往短信mq中插入數據方式,讓短信服務來消費mq發短信;

此時插入mq消息的服務爲了保證給所有用戶發消息,並且要在短時間內插入完成(因此用到了異步插入方式(快速)),我們就需要知道每次插入mq是否成功,如果不成功那我們可以收集失敗的信息後補發(因此confirm模式排上了用場);如圖設計:

 在springboot中可以使用基於amqp封裝的工廠類來開啓confirm模式,然後通過RabbitTemplate模板來設置回調函數,如下代碼:

 1     ///region producer生產 - confirm模式
 2 
 3     public RabbitTemplate getRabbitTemplate(RabbitTemplate.ConfirmCallback confirmCallback) {
 4         return this.getRabbitTemplate(this.connectionFactory(), confirmCallback);
 5     }
 6 
 7     public RabbitTemplate getRabbitTemplate(CachingConnectionFactory connectionFactory, RabbitTemplate.ConfirmCallback confirmCallback) {
 8         RabbitTemplate template = new RabbitTemplate(connectionFactory);
 9         //product開啓confirm模式
10         connectionFactory.setPublisherConfirms(true);
11         //設置confirm回調處理
12         template.setConfirmCallback(confirmCallback);
13         return template;
14     }
15     ///endregion

這裏通過RabbitTemplate.ConfirmCallback函數編程來傳遞我們自定義的回調方法,如下收集confirm返回的結果信息:

1         RabbitUtil rabbitUtil = new RabbitUtil(this.getFirstNode().getLink());
2         RabbitTemplate template = rabbitUtil.getRabbitTemplate((a, b, c) -> {
3             System.out.println("firstNodeTpl - ConfirmCallback的Id:" + a.getId() + ";狀態:" + b + ";信息:" + c);
4         });

最後再通過RabbitTemplate實例的convertAndSend方法發送mq信息,我們能夠在日誌中看到如下記錄的信息:

這裏的狀態true:表示send成功,false:表示send失敗;通常false的時候信息c會有響應的錯誤提示,這裏把網絡斷開,如下錯誤提示:

consumer的ack模式

再來,有這樣一種場景2:短信服務去消費mq隊列信息時,倘若服務調用的運營商發送短信接口異常了(短信運營商接口欠費),我們此時的短信是發送失敗的,用戶也收不到短信,但是在默認(默認開啓ack)前提下mq消息已經被消費了rabbit中沒有記錄了(kafka例外);想要mq消息在業務邏輯異常時還存在,那麼可以使用ack方式;

在springboot中可以使用基於amqp封裝的工廠類關閉自動ack模式,改爲手動ack方式;只有當業務代碼流程走完後,最後通過代碼設置ack標識,來通知rabbit消息可以丟棄了;如果設置了手動模式後,又沒有提交ack標識,那麼mq中的消息一直存在無法釋放(每次consumer消費後,rabbit會把noack的消息重複放入隊列中):

 1     ///region consumer監聽 - 手動ack
 2     public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
 3         return this.listenerContainerFactory(this.connectionFactory());
 4     }
 5 
 6     public SimpleRabbitListenerContainerFactory listenerContainerFactory(ConnectionFactory connectionFactory) {
 7         SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
 8         factory.setConnectionFactory(connectionFactory);
 9         //代碼手動ack
10         factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
11         //開啓消費者數量
12         factory.setConcurrentConsumers(2);
13         //每次接受數據量,默認250
14         factory.setPrefetchCount(300);
15         return factory;
16     }
17     ///endregion

通過連接工廠設置手動ack方式,然後獲取mq消息後,走完正常業務邏輯,最後再手動通知ack釋放消息,如下:

1     @RabbitListener(containerFactory = "firstNodeListener", queues = {"${shenniu.rabbits.firstNode.queue}"})
2     private void firstNodeListener(String msg, Channel channel, Message message) {
3         try {
4             long deliverTag = message.getMessageProperties().getDeliveryTag();
5             System.out.println("firstNodeListener - 消費消息 [" + deliverTag + "] - " + msg);
6             channel.basicAck(deliverTag, true);
7         } catch (Exception ex) {
8         }
9     }

這裏ack主要根據mq消息的唯一編號(deliverTag)來通知;如果我們不設置ack確認,那麼消息狀態會是這樣如下rabbit管理後臺:

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