? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?原創者:文思
網絡資料(基本方向):
1:當出現異常時,我們需要把這個消息回滾到消息隊列要么拋棄此消息:
2:經過開發中的實際測試,當讓消息回滾到消息隊列時,這條消息不會回到隊列尾部,而是仍是在隊列頭部,這時消費者會立馬又接收到這條消息,進行處理,接著拋出異常,進行回滾,如此反復進行。這種情況會導致消息隊列處理出現阻塞,消息堆積,導致正常消息也無法運行。對于消息回滾到消息隊列,我們希望比較理想的方式時出現異常的消息到達消息隊列尾部,這樣既保證消息不會丟失,又保證了正常業務的進行,因此我們采取的解決方案是,將消息進行應答,這時消息隊列會刪除該消息,同時我們再次發送該消息到消息隊列,這時就實現了錯誤消息進行消息隊列尾部的方案。
實際應用:
在做jodconvert結合mq進行文檔異步轉換時,因在process函數中沒有捕獲convert方法(文檔轉換時的方法)中拋出的異常,導致mq的消費方(process函數)接收消息消費時失敗->返回隊列第一位->執行->消費失敗->返回隊列第一位,如此反復導致后續mq消息被阻塞無法接收執行,用catch()捕獲異常后,線應答成功,不會再次進入隊列執行,解決。
以此總結出mq自動應答的1個知識點:
Spring 與RabbitMq集成對消息的處理方式是默認自動應答(百度),但是mq消息消費與否的標志,網絡上百度出來的資料有些是錯誤的,現在通過程序來進行驗證和判斷,結論是:以process函數是否拋出異常或者異常是否被catch捕獲為標準,以下是程序推論:
在使用mq消息進行業務處理的過程中,也就是使用消息進行業務處理的時候(convertUrl = queueBusinessHandlerService.ms2pdfByQueueExternalUtil(inputUrl)),什么情況下系統會認為消息消費不會失敗,已經應答了呢,現在驗證異常依次上拋,在最外層捕獲(異常一定要依次通過throw拋出,否則程序就因為發生異常而中斷):
1
1.1
1.1.
1.1.1
1.1.1.1
從1.1.1.1依次上拋到1
然后在最外層捕獲:
通過命令行看日志可以看到,不會再執行那條mq記錄了,也就說明:
當convertUrl = queueBusinessHandlerService.ms2pdfByQueueExternalUtil(inputUrl)拋出異常時被catch捕獲,則process方法會認為消息消費成功,因為異常被捕獲了嘛,process程序體沒有中斷,執行完了,所以認為消費成功,進行了自動應答,即使業務函數處理失敗了,也不在會出現隊列中了。
如果需要對失敗的記錄進行處理,建議在catch{}中:
//手動進行應答channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
//重新發送消息到隊列尾部channel.basicPublish(message.getMessageProperties().getReceivedExchange(),message.getMessageProperties().getReceivedRoutingKey(), MessageProperties.PERSISTENT_TEXT_PLAIN,JSON.toJSONBytes(new Object()));
不需要再次處理的就不用管了。
什么情況下算消費失敗,沒有應答成功呢,如果不在process中捕獲異常,或者在try{}catch(){}以外的地方發生異常,如下:
process中報了異常且沒有被catch捕獲,則process認為消息接收時出現錯誤,沒有消費成功,則自動返回到隊里第一位,再次重復執行:
.
反復回到隊列重復執行...
.
在使用spring boot時,默認的是自動應答,如果想手工應答,在application.propretes中:
spring.rabbitmq.listener.acknowledge-mode=manual
則:
mq順利執行完沒有報錯,但再次發送mq:
無法接收到新的mq消息。就需要在程序中進行手工消息應該操作: