Spring-RabbitMQ Consumer踩坑经历

Spring-RabbitMQ Consumer踩坑经历

问题1: Consumer假死,无真正的消费能力

背景
spring-rabbit 版本变更至 1.6.2.RELEASE

现象
consumer数量正常,mq控制面板的 prefetch 参数始终是1, 消息无法正常ack, 队列处于假死状态 系统报异常org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException

[2018-09-09 10:31:27.27]RuntimeException-org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException [ ERROR] [ Elog ]
Execution of Rabbit message listener failed. org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:870)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:780)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:700)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:95)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:187)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1187)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:681)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1165)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1149)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1100(SimpleMessageListenerContainer.java:95)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1312)
	at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.amqp.AmqpIllegalStateException: No default listener method specified: Either specify a non-null value for the 'defaultListenerMethod' property or override the 'getListenerMethodName' method.
	at org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:291)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:777)
	... 10 more
复制代码

异常原因
rabbit:listener的ID 和 真实consumer的ID 冲突,导致真实的consumer Bean无效,找不到接受消息的方法,而rabbitmq在与clien端通信过程中发生异常,会停止消费。

<bean id="consumer_1" class="me.ele.Consumer1"/>
<rabbit:listener-container connection-factory="galaxyConnectionFactory" acknowledge="manual" concurrency="16" >
        <rabbit:listener queues="queue_1" ref="consumer_1" id="consumer_1" />
    </rabbit:listener-container>
复制代码

解决办法

删除rabbit:listener 的ID属性

问题2:consumer数量异常

背景
原服务有6个队列,每个队列起10个consumer, task-executor线程池设置为90, 后因数据量增加,发现消费能力不足,决定增加consumer数量,调整listener-container的concurrency为20,重启服务器。

现象
部分队列consumer数量不足,缺失项始终为xml中声明在后的队列

异常定位
回退concurrency参数为3异常消失,观察异常现场,发现consumer消失队列有先后顺序之分,且consumer数量存在上限值为90,猜测是收参数限制,检查配置参数如下:

    <bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"
      id="taskExecutor">
        <!--核心线程数 -->
        <property name="corePoolSize" value="16"/>
        <!--最大线程数 -->
        <property name="maxPoolSize" value="16"/>
        <property name="queueCapacity" value="500"/>
        <!--线程池维护线程所允许的空闲时间 -->
        <property name="keepAliveSeconds" value="60"/>
        <!--线程池对拒绝任务(无线程可用)的处理策略 -->
        <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
        </property>
        <property name="WaitForTasksToCompleteOnShutdown" value="true"/>
    </bean>
    
    <rabbit:listener-container connection-factory="monitorConnectionFactory" 
            acknowledge="manual" task-executor="taskExecutor" prefetch="10" concurrency="20">
        <rabbit:listener queues="queue_1" ref="consumer_1"/>
        <rabbit:listener queues="queue_2" ref="consumer_2"/>
        <rabbit:listener queues="queue_3" ref="consumer_3"/>
        <rabbit:listener queues="queue_4" ref="consumer_4"/>
        <rabbit:listener queues="queue_5" ref="consumer_5"/>
        <rabbit:listener queues="queue_6" ref="consumer_6"/>
    </rabbit:listener-container>
    
复制代码

异常原因
经排查对比,发现上限值与task-executor ThreadPoolTaskExecutor参数corePoolSize、maxPoolSize极为接近,查询相关资料发现,多个queue的consumer会共用taskExecutor的线程池数量,如果线程池数量不足,consumer无法创建

解决办法
增大task-executor corePoolSize和maxPoolSize的值为200,重启服务,解决。

总结

  1. 在spring容器管理中,ID是一个对象的引用,必须仅且只有一个无重复的ID, 包含Bean及其他自定义标签(如rabbit:listener), 即使在不同文件也需要注意。
  2. 对共享bean的使用,需额外注意共享bean的资源分配。

转载于:https://juejin.im/post/5b94d290e51d450e8d762c37

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Spring Boot 中使用 RabbitMQ 时,我们可以通过在配置文件中配置绑定关系,将交换机和队列进行绑定。具体步骤如下: 1. 在 application.properties 或 application.yml 中配置 RabbitMQ 连接信息: ``` spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest ``` 2. 创建一个交换机和一个队列,并将它们绑定在一起: ``` @Configuration public class RabbitConfig { @Bean public Queue queue() { return new Queue("myqueue", true); } @Bean public DirectExchange exchange() { return new DirectExchange("myexchange"); } @Bean public Binding binding(Queue queue, DirectExchange exchange) { return BindingBuilder.bind(queue).to(exchange).with("mykey"); } } ``` 3. 在需要发送消息的地方,注入 RabbitTemplate 并调用 convertAndSend 方法: ``` @Autowired private RabbitTemplate rabbitTemplate; public void sendMessage(String message) { rabbitTemplate.convertAndSend("myexchange", "mykey", message); } ``` 4. 在需要接收消息的地方,注入 SimpleMessageListenerContainer 并实现 MessageListener 接口: ``` @Autowired private Queue queue; @Bean public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.setQueueNames(queue.getName()); container.setMessageListener(listenerAdapter); return container; } @Bean public MessageListenerAdapter listenerAdapter() { return new MessageListenerAdapter(new MyMessageListener()); } public class MyMessageListener implements MessageListener { @Override public void onMessage(Message message) { String body = new String(message.getBody()); System.out.println("Received message: " + body); } } ``` 通过以上步骤,我们就可以实现交换机和队列的绑定,以及在队列中发送和接收消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值