RabbitMq報錯reply-code=406 reply-text=PRECONDITION_FAILED解決
一、檢查配置是否正確
今天看生產(chǎn)日志發(fā)現(xiàn)了很多的RabbitMq報錯:reply-code=406, reply-text=PRECONDITION_FAILED - unknown delivery tag 1, class-id=60, method-id=80,全都是異常之后重復(fù)消費,重復(fù)報錯。
unknown delivery tag 1-參考rabbitMQ的tag機制可知這條消息已經(jīng)完成了消費;那么出現(xiàn)這個原因是配置不對了。
首先排查SimpleRabbitListenerContainerFactory是否加上手動ack
private SimpleRabbitListenerContainerFactory getSimpleRabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
//使用jackson進行消息序列與反序列
factory.setMessageConverter(new Jackson2JsonMessageConverter());
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 開啟手動 ack
return factory;
}
Jackson2JsonMessageConverter是rabbitmq 對java對象json序列化的支持,在發(fā)送消息時,會先將自定義的消息類序列化成json格式,再轉(zhuǎn)成byte構(gòu)造 Message,rabbitmq的ack就被設(shè)置為自動提交;需要手動開啟ack,沒得問題:factory.setAcknowledgeMode(AcknowledgeMode.MANUAL)。
其次檢查配置文件,配置文件設(shè)置為手動ack
spring.rabbitmq.listener.direct.acknowledge-mode=manual
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
###開啟消息確認(rèn)機制 confirms
virtual-host: /
publisher-confirms: true
publisher-returns: true
#rabbitmq配置加上手動應(yīng)答
listener:
simple:
acknowledge-mode: manual
二、rabbitmq消息重回隊列,回到隊尾
當(dāng)出現(xiàn)異常時,我們需要把這個消息回滾到消息隊列,有兩種方式:
- ack返回false,并重新回到隊列channel.basicNack channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
- 拒絕消息channel.basicReject channel.basicReject(message.getMessageProperties().getDeliveryTag(), true)
但是環(huán)境中的實際情況是,當(dāng)消息回滾到消息隊列時,這條消息不會回到隊列尾部,而是仍是在隊列頭部,這時消費者會立馬又接收到這條消息進行處理,接著拋出異常,進行回滾,如此反復(fù)進行。這種情況會導(dǎo)致消息隊列處理出現(xiàn)阻塞,消息堆積,導(dǎo)致正常消息也無法消費。
對于消息回滾到消息隊列,我們希望方式是出現(xiàn)異常的消息到達消息隊列尾部,這樣既保證消息不會丟失,又保證了正常業(yè)務(wù)的進行。
因此我們采取的解決方案是,將消息先進行應(yīng)答,這時消息隊列會刪除該消息,同時再次發(fā)送該消息到消息隊列,這時就實現(xiàn)了錯誤消息進行消息隊列尾部的方案。
具體方案為:
//手動進行應(yīng)答 channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); //重新發(fā)送消息到隊尾 channel.basicPublish(message.getMessageProperties().getReceivedExchange(), message.getMessageProperties().getReceivedRoutingKey(), MessageProperties.PERSISTENT_TEXT_PLAIN, JSON.toJSONBytes(new Object()));
三、消息體本身有誤
如果一個消息體本身有誤,會導(dǎo)致該消息體,一直無法進行處理,而服務(wù)器中刷出大量無用日志
解決這個問題可以采取兩種方案:
1、對于日常細(xì)致處理,分清哪些是可以恢復(fù)的異常,哪些是不可以恢復(fù)的異常。對于可以恢復(fù)的異常我們采取第三條中的解決方案,對于不可以處理的異常,我們采用記錄日志,直接丟棄該消息方案。
2、對每條消息進行標(biāo)記,記錄每條消息的處理次數(shù),當(dāng)一條消息,多次處理仍不能成功時,處理次數(shù)到達我們設(shè)置的值時,我們就丟棄該消息,但需要記錄詳細(xì)的日志。
消息監(jiān)聽內(nèi)的異常處理有兩種方式:
1、內(nèi)部catch后直接處理,然后使用channel對消息進行確認(rèn)
2、配置RepublishMessageRecoverer將處理異常的消息發(fā)送到指定隊列專門處理或記錄。監(jiān)聽的方法內(nèi)拋出異常貌似沒有太大用處。因為拋出異常就算是重試也非常有可能會繼續(xù)出現(xiàn)異常,當(dāng)重試次數(shù)完了之后消息就只有重啟應(yīng)用才能接收到了,很有可能導(dǎo)致消息消費不及時。當(dāng)然可以配置RepublishMessageRecoverer來解決,但是萬一RepublishMessageRecoverer發(fā)送失敗了呢。那就可能造成消息消費不及時了。所以即使需要將處理出現(xiàn)異常的消息統(tǒng)一放到另外隊列去處理,個人建議兩種方式:
- catch異常后,手動發(fā)送到指定隊列,然后使用channel給rabbitmq確認(rèn)消息已消費
- 給Queue綁定死信隊列,使用nack(requque為false)確認(rèn)消息消費失敗
以上就是RabbitMq報錯reply-code=406 reply-text=PRECONDITION_FAILED解決的詳細(xì)內(nèi)容,更多關(guān)于RabbitMq reply報錯解決的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IDEA 非常重要的一些設(shè)置項(一連串的問題差點讓我重新用回 Eclipse)
這篇文章主要介紹了IDEA 非常重要的一些設(shè)置項(一連串的問題差點讓我重新用回 Eclipse),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08
java數(shù)據(jù)結(jié)構(gòu)和算法學(xué)習(xí)之漢諾塔示例
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)和算法中的漢諾塔示例,需要的朋友可以參考下2014-02-02
SpringBoot JVM參數(shù)調(diào)優(yōu)方式
這篇文章主要介紹了SpringBoot JVM參數(shù)調(diào)優(yōu)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09

