RocketMQ常见错误与解决方案

本文介绍了RocketMQ在处理主题路由、消息发送超时以及SystemBusy错误时的机制和解决方案。包括检查Topic是否存在、Broker的自动创建Topic设置、客户端与Nameserver的连接一致性,以及如何调整发送超时时间和重试策略。对于PageCache繁忙问题,提出了启用transientStorePoolEnable和集群扩容的解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、No route info of this topic

  • 如果Broker开启了自动创建Topic,在启动的时候会默认创建主题:TBW102,并会随着Broker发送到Nameserver的心跳包汇报给Nameserver,继而从Nameserver查询路由信息时能返回路由信息。
  • 消息发送者在消息发送时首先会查本地缓存,如果本地缓存中存在,直接返回路由信息。
  • 如果缓存不存在,则向Nameserver查询路由信息,如果Nameserver存在该路由信息,就直接返回。
  • 如果Nameserver不存在该topic的路由信息,如果没有开启自动创建主题,则抛出 No route info of this topic。
  • 如果开启了自动创建主题,则使用默认主题向Nameserver查询路由信息,并使用默认Topic的路由信息为自己的路由信息,将不会抛出 No route info of this topic。

解决思路

1. 通过rocketmq-console查询Topic是否存在

2. 查看配置文件, Broker是否开启了自动创建Topic,参数为:autoCreateTopicEnable, 该参数默认为true,但在生产环境不建议开启。

3. 如果开启了自动创建Topic,但还是抛出这个错误,这个时候请检查客户端(Producer)连接的Nameserver地址是否与Broker中配置的nameserver地址是否一致。

2、消息发送超时

RocketMQ每一分钟打印前一分钟内消息发送的耗时情况分布

如果100-200ms及以上的区间超过20个后,说明Broker确实存在一定的瓶颈 

  • [<=0ms] 小于0ms,即微妙级别的。
  • [0~10ms] 小于10ms的个数。
  • [10~50ms] 大于10ms小
  • 于50ms的个数
cd /home/logs/rocketmqlogs/
grep -n 'PAGECACHERT' store.log | more

解决思路

减少消息发送的超时时间,增加重试次数,并增加快速失败的最大等待时长

1. 快速失败导致的错误为SYSTEM_BUSY,并不会触发重试,适当增大Broker端快速失败的时长

#该值默认为200,表示200ms
waitTimeMillsInSendQueue=1000

2. 如果RocketMQ的客户端版本为4.3.0以下版本(不含4.3.0) 将超时时间设置消息发送的超时时间为500ms,并将重试次数设置为6次(这个可以适当进行调整,尽量大于3)

    DefaultMQProducer producer = new DefaultMQProducer("dw_test_producer_group");
    producer.setNamesrvAddr("127.0.0.1:9876");
    producer.setRetryTimesWhenSendFailed(5);// 同步发送模式:重试次数
    producer.setRetryTimesWhenSendAsyncFailed(5);// 异步发送模式:重试次数
    producer.start();
    producer.send(msg,500);// 消息发送超时时间

3. 如果RocketMQ的客户端版本为4.3.0及以上版本 如果客户端版本为4.3.0及其以上版本,由于其设置的消息发送超时时间为所有重试的总的超时时间,故不能直接通过设置RocketMQ的发送API的超时时间,而是需要对其API进行包装,重试需要在外层收到进行

    public static SendResult send(DefaultMQProducer producer, Message msg, int retryCount) {
        Throwable e = null;
        for (int i = 0; i < retryCount; i++) {
            try {
                return producer.send(msg, 500); //设置超时时间,为500ms,内部有重试机制
            } catch (Throwable e2) {
                e = e2;
            }
        }
        throw new RuntimeException("消息发送异常", e);
    }

配置application.yaml 

rocketmq:
  name-server: http://127.0.0.0:11800
  producer:
    group: topic_receive_default_test # 必须指定group
    send-message-timeout: 30000 # 消息发送超时时长,默认3s
    retry-times-when-send-failed: 3 # 同步发送消息失败重试次数,默认2
    retry-times-when-send-async-failed: 3 # 异步发送消息失败重试次数,默认2

3、System busy、Broker busy

常见错误

  • [REJECTREQUEST]system busy
  • too many requests and system thread pool busy
  • [PC_SYNCHRONIZED]broker busy
  • [PCBUSY_CLEAN_QUEUE]broker busy
  • [TIMEOUT_CLEAN_QUEUE]broker busy

相关说明

  • 判断pagecache是否忙的依据就是在写入消息时,在向内存追加消息时加锁的时间,默认的判断标准是加锁时间超过1s,就认为是pagecache压力大,向客户端抛出相关的错误日志。
  • 发送线程池挤压的拒绝策略 在RocketMQ中处理消息发送的是一个只有一个线程的线程池,内部会维护一个有界队列,默认长度为1W,如果当前队列中挤压的数量超过1w,执行线程池的拒绝策略,从而抛出[too many requests and system thread pool busy]错误。
  • Broker端快速失败 默认情况下Broker端开启了快速失败机制,就是在Broker端还未发生pagecache繁忙(加锁超过1s)的情况,但存在一些请求在消息发送队列中等待200ms的情况,RocketMQ会不再继续排队,直接向客户端返回system busy,但由于rocketmq客户端目前对该错误没有进行重试处理,所以在解决这类问题的时候需要额外处理。

PageCache繁忙解决方案

在broker中配置文件中增加如下配置, 开启transientStorePoolEnable机制

transientStorePoolEnable=true 
  • 消息先写入到堆外内存中,该内存由于启用了内存锁定机制,故消息的写入是接近直接操作内存,性能能得到保证。
  • 消息进入到堆外内存后,后台会启动一个线程,一批一批将消息提交到pagecache,即写消息时对pagecache的写操作由单条写入变成了批量写入,降低了对pagecache的压力。 引入transientStorePoolEnable会增加数据丢失的可能性,如果Broker JVM进程异常退出,提交到PageCache中的消息是不会丢失的,但存在堆外内存(DirectByteBuffer)中但还未提交到PageCache中的这部分消息,将会丢失。但通常情况下,RocketMQ进程退出的可能性不大,通常情况下,如果启用了transientStorePoolEnable,消息发送端需要有重新推送机制(补偿思想)。
  • 扩容 如果在开启了transientStorePoolEnable后,还会出现pagecache级别的繁忙,那需要集群进行扩容,或者对集群中的topic进行拆分,即将一部分topic迁移到其他集群中,降低集群的负载。

在RocketMQ出现pagecache繁忙造成的broker busy,RocketMQ Client会有重试机制。不建议开启该机制,尽量使用扩容解决

TIMEOUT_CLEAN_QUEUE 解决方案

在broker的配置文件中增加如下配置,适当增大快速失败的判断标准

#该值默认为200,表示200ms
waitTimeMillsInSendQueue=1000
### RocketMQ 常见问题及解决方法 #### 一、消息幂等问题 在分布式系统中,由于网络抖动或其他异常情况可能导致消息重复发送或多次消费。为了应对这一问题,通常需要实现消费幂等。 - **什么是消费幂等** 消费幂等是指同一条消息无论被消费多少次,其最终的结果是一致的[^3]。 - **消息重复的场景分析** - 发送消息重复:在网络不稳定的情况下,可能会导致消息重复发送[^3]。 - 消费时消息重复:消费者在处理过程中可能出现异常中断,重新拉取消息后再次消费。 - Rebalance时消息重复:当消费者组成员发生变化时,可能引发消息重复分配和消费[^3]。 - **通用解决方案** 实现消费幂等的关键在于两个要素:幂等令牌唯一性处理逻辑[^3]。通过记录已消费过的消息ID或者业务流水号等方式,确保同一消息不会被执行两次操作。 --- #### 二、消息堆积问题 消息堆积通常是由于生产速度快于消费速度造成的。以下是常见的原因及其优化措施: - **产生原因分析** - 消息拉取效率低下:可能是由于网络带宽不足或客户端配置不合理所致[^2]。 - 消息消费耗时过长:某些复杂业务逻辑增加了单条消息的平均处理时间[^2]。 - 消费并发度过低:默认情况下消费者的线程池大小有限制,无法充分利用硬件资源。 - **如何避免消息堆积和消费延迟** - 提高消费能力:增加消费者的实例数量以分摊负载;调整`consumeThreadMin` 和 `consumeThreadMax` 参数来动态扩展消费线程数[^4]。 - 调优业务逻辑:减少每条消息的实际执行开销,比如批量提交数据库更新而不是逐条写入[^4]。 - 设置合理的超时机制:对于长时间未完成的任务可以主动放弃并放入死信队列进一步排查[^4]。 --- #### 三、消息清理问题 随着时间推移,存储空间会被大量历史数据占据。因此定期清除陈旧无用的数据成为必要之举。 - Broker提供了TTL(Time To Live)参数用于定义消息存活期限,超过该时间段后的消息将会自动删除[^3]。 --- #### 四、消息过滤问题 根据具体需求筛选感兴趣的内容有助于降低不必要的传输成本。 - **Tag方式过滤**: 使用标签标记不同类型的消息,在订阅阶段指定感兴趣的tag即可达到目的。 - **SQL表达式过滤**: 支持基于属性字段构建复杂的条件语句来进行精确匹配。 示例代码展示两种不同类型的过滤器应用: ```java // Tag 过滤 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group"); consumer.subscribe("TopicTest", "TagA || TagB"); // SQL 表达式过滤 consumer.subscribe("TopicTest", "price >= 5 AND city='Beijing'"); ``` --- #### 五、消息重试问题 无论是因为瞬态错误还是永久失效都需要有相应的恢复手段保障服务质量。 - 对于普通的乱序消息,默认支持最多16次指数退避式的再尝试机会[^2]; - 如果涉及严格的因果关系维护则需启用有序模式下的特别策略——一旦发现失败即暂停后续项直到当前障碍解除为止[^2]。 特殊情况如超出最大允许范围仍未成功,则转入专门设立的DLQ(Dead Letter Queue)[^4]供人工介入审查。 --- #### 六、如何保证消息不丢失 这是衡量一款中间件产品成熟度的重要指标之一。 - Producer层面可通过开启事务特性确认投递状态后再做下一步动作; - Consumer方面除了常规的心跳检测外还应周期性的向远程汇报进度以防万一中途崩溃遗漏部分已完成的工作成果[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值