本文将在 RocketMQ 消息发送system busy、broker busy缘由分析与解决方案 的基础上,结合生产上的日志尝试再次理解 broker busy 以及探讨解决方案。java
首先,broker busy 相关的日志关键字以下:服务器
上述前面4个关键字在上篇文章中已详细介绍,本文先对出现上述错误进行一个总结,具体的分析过程请查阅上篇文章。并发
本文先给出一张流程图,展现上述5种 broker busy 分别会在消息发送的哪一个阶段抛出,以便你们可以清晰的了解其发生的缘由。 性能
针对前4种 broker busy 出现的问题已经在上篇文章中详细介绍,主要是因为 Broker 在追加消息时持有的锁时间超过了设置的1s,Broker 为了自我保护,会抛出错误,客户端会选择其余 broker 服务器进行重试。若是对不是金融级服务,建议将 transientStorePoolEnable = true,能够有效避免前面 4 种 broker ,由于开启这个参数,消息首先会存储在堆外内存中,而且 RocketMQ 提供了内存锁定的功能,其追加性能能获得必定的保障,这样能够作到在内存使用层面的读写分离,即写消息是直接写入堆外内存,消费消息直接从 pagecache中读,而后定时将堆外内存的消息写入 pagecache。但这种方案随之带来的就是可能存在消息丢失,若是对消息很是严谨的话,建议扩容集群,或迁移topic到新的集群。this
同时在作 Broker 服务器巡检的时候,能够经过去经过以下命令去查看 broker 一次消息追加是否会超过 500 ms。 .net
在这个图中咱们看到在设置了 transientStorePoolEnable 为 true 的状况下,虽然一天只有一条超过500ms的消息,但也值得警戒了,因为对系统内核参数掌握程度不够,这种状况,估计只能走集群扩容的路子了。但若是一天消息量巨大并且出现频率不高的状况,因为有重试机制,倒不会带来太大的问题。若是出现太多的错误,建议集群扩容。3d
本文接下来想重点探讨一下 [TIMEOUT_CLEAN_QUEUE]broker busy 这种状况。日志
BrokerFastFailure#cleanExpiredRequestcode
while (true) { try { if (!this.brokerController.getSendThreadPoolQueue().isEmpty()) { final Runnable runnable = this.brokerController.getSendThreadPoolQueue().peek(); if (null == runnable) { break; } final RequestTask rt = castRunnable(runnable); if (rt == null || rt.isStopRun()) { break; } final long behind = System.currentTimeMillis() - rt.getCreateTimestamp(); if (behind >= this.brokerController.getBrokerConfig().getWaitTimeMillsInSendQueue()) { if (this.brokerController.getSendThreadPoolQueue().remove(runnable)) { rt.setStopRun(true); rt.returnResponse(RemotingSysResponseCode.SYSTEM_BUSY, String.format("[TIMEOUT_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: %sms, size of queue: %d", behind, this.brokerController.getSendThreadPoolQueue().size())); } } else { break; } } else { break; } } catch (Throwable ignored) { } }
能够看出来,抛出这种错误,在 broker 尚未发送“严重”的 pagecache 繁忙,即消息追加到内存中的最大时延没有超过 1s,一般追加是很快的,绝大部分都会低于1ms,但可能会因为出现一个超过200ms的追加时间,致使排队中的任务等待时间超过了200ms,则此时会触发broker 端的快速失败,让请求快速失败,便于客户端快速重试。可是这种请求并非实时的,而是每隔10s 检查一遍。orm
值得注意的是,一旦出现 TIMEOUT_CLEAN_QUEUE,可能在一个点会有多个这样的错误信息,具体多少与当前积压在待发送队列中的个数有关。
关于 [TIMEOUT_CLEAN_QUEUE]broker busy 咱们也能够适当调整 waitTimeMillsInSendQueue,默认值为200ms,能够适当调整到400ms。
若是你们以为文章对您有帮助的话,麻烦帮忙点个赞,谢谢。
> 做者介绍:《RocketMQ技术内幕》做者,维护公众号:中间件兴趣圈,目前主要发表了源码阅读java集合、JUC(java并发包)、Netty、ElasticJob、Mycat、Dubbo、RocketMQ、mybaits等系列源码。