Rabbitmq消息被消费时抛异常,进入Unacked 状态,进而导致消费者不断尝试消费(上)

一、背景

在对阿里云成本分析的时候,发现SLS日志的费用暴增,由平均每月的2000元突然增至6000多。

查看日志的费用明细,按应收金额降序得知,原来是某个java服务打印的jvm日志暴增。

再已进入SLS查看打印的日志量,更是惊呆了。。。

不仅是打印的数量多,打印的时间点是遍布整天24小时。

也就是说,该日志是整天整夜地打印,像是进入了死循环。

围绕着这个问题,尝试让AI给我回答原因是什么。

可能是我提问水平不够,未能准确得出答案,在排查这个问题的过程中,代码是一分钟就定位出。

但是,围绕着mq消费者是如何消费消息的原理,总是不甚明了。

于是,我整理了两篇文章,希望能把个问题说明清楚,最后也会对此作出总结。

二、报错日志

ntContainer#2-1\] s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message listener failed. org.springframework.amqp.rabbit.support.ListenerExecutionFailedException

详细错误:

java 复制代码
[ntContainer#2-1] s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message listener failed.

org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Listener method 'public void com.xxx.interfaces.listener.XxxQueueListener.xxx(java.lang.String)' threw exception
	at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:227)
	at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandlerAndProcessResult(MessagingMessageListenerAdapter.java:147)
	at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:132)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1579)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1498)
	at sun.reflect.GeneratedMethodAccessor174.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at brave.spring.rabbit.TracingRabbitListenerAdvice.invoke(TracingRabbitListenerAdvice.java:101)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
	at org.springframework.amqp.rabbit.listener.$Proxy360.invokeListener(Unknown Source)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1486)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1477)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1421)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:958)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:908)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:81)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1279)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1185)
	at java.lang.Thread.run(Thread.java:748)
	
Caused by: com.xxx.common.exception.ResponseException: 资源不存在或已被删除
	
	at sun.reflect.GeneratedMethodAccessor175.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171)
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:120)
	at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:53)
	at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:219)
	... 23 common frames omitted

代码定位:

消息监听者在消费消息的时候,业务判断的时候,对于非法的数据进行抛异常ResponseException, 提示资源不存在或已被删除。

要知道消息在首次消费后,下一步会做什么,就必须得看看程序的mq配置项了。

三、rabbitmq配置项

bash 复制代码
spring:
    rabbitmq:
        host: xxx
        username: xxxx
        port: 5672
        publisher-confirms: true
        virtual-host: xxxxx

这里既没有配置acknowledge-mode确认模式,也没有配置retry重试策略。

也就是说,一个mq消息状态要从Ready状态转入完成状态,前提是消息监听者正常执行。

像本例中,由于抛异常,会导致该mq消息的状态从Ready状态转入Unacked状态。

四、rabbitmq监控

1、投递mq消息

此时,消息队列中有一个消息,其状态是Ready。

2、开始监听mq消息

java程序由于抛出异常,不断打印上述错误日志。

而此时的mq消息状态则是:

消息的状态已变成了Unacked,队列的消息总数也才1个,但是消息投递速度却停不下来。

究其原因,是mq消息进入投递的死循环,在消费的时候抛异常,导致未能消费成功消息。

3、停止监听mq消息

当java应用停止时,mq消息的状态就又从Unacked回到了Ready状态。

此时消息队列的投递速度又重归于零了。

因为没有了消息监听者。

下一篇,我们将进一步分析该问题,并总结一下mq消息消费的整个过程,以及我们应该如何合理配置。

相关推荐
Cyanto7 分钟前
Java并发编程面试题
java·开发语言·面试
不超限13 分钟前
Asp.net core 使用EntityFrame Work
后端·asp.net
在未来等你19 分钟前
互联网大厂Java求职面试:AI大模型与云原生技术的深度融合
java·云原生·kubernetes·生成式ai·向量数据库·ai大模型·面试场景
豌豆花下猫25 分钟前
Python 潮流周刊#105:Dify突破10万星、2025全栈开发的最佳实践
后端·python·ai
sss191s35 分钟前
Java 集合面试题从数据结构到 HashMap 源码剖析详解及常见考点梳理
java·开发语言·数据结构
LI JS@你猜啊43 分钟前
window安装docker
java·spring cloud·eureka
书中自有妍如玉1 小时前
.net 使用MQTT订阅消息
java·前端·.net
风铃儿~1 小时前
Spring AI 入门:Java 开发者的生成式 AI 实践之路
java·人工智能·spring
斯普信专业组1 小时前
Tomcat全方位监控实施方案指南
java·tomcat
忆雾屿2 小时前
云原生时代 Kafka 深度实践:06原理剖析与源码解读
java·后端·云原生·kafka