再谈 RocketMQ broker busy(实战篇)
本文将在 RocketMQ 消息发送system busy、broker busy原因分析与解决方案 的基础上,结合生产上的日志尝试再次理解 broker 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
上述前面4个关键字在上篇文章中已详细介绍,本文先对出现上述错误进行一个总结,具体的分析过程请查阅上篇文章。
本文先给出一张流程图,展示上述5种 broker busy 分别会在消息发送的哪个阶段抛出,以便大家能够清晰的了解其发生的原因。
针对前4种 broker busy 出现的问题已经在上篇文章中详细介绍,主要是由于 Broker 在追加消息时持有的锁时间超过了设置的1s,Broker 为了自我保护,会抛出错误,客户端会选择其他 broker 服务器进行重试。如果对不是金融级服务,建议将 transientStorePoolEnable = true,可以有效避免前面 4 种 broker ,因为开启这个参数,消息首先会存储在堆外内存中,并且 RocketMQ 提供了内存锁定的功能,其追加性能能得到一定的保障,这样可以做到在内存使用层面的读写分离,即写消息是直接写入堆外内存,消费消息直接从 pagecache中读,然后定时将堆外内存的消息写入 pagecache。但这种方案随之带来的就是可能存在消息丢失,如果对消息非常严谨的话,建议扩容集群,或迁移topic到新的集群。
同时在做 Broker 服务器巡检的时候,可以通过去通过如下命令去查看 broker 一次消息追加是否会超过 500 ms。
在这个图中我们看到在设置了 transientStorePoolEnable 为 true 的情况下,虽然一天只有一条超过500ms的消息,但也值得警惕了,由于对系统内核参数掌握程度不够,这种情况,估计只能走集群扩容的路子了。但如果一天消息量巨大而且出现频率不高的情况,由于有重试机制,倒不会带来太大的问题。如果出现太多的错误,建议集群扩容。
本文接下来想重点探讨一下 [TIMEOUT_CLEAN_QUEUE]broker busy 这种情况。
BrokerFastFailure#cleanExpiredRequest
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 检查一遍。
值得注意的是,一旦出现 TIMEOUT_CLEAN_QUEUE,可能在一个点会有多个这样的错误信息,具体多少与当前积压在待发送队列中的个数有关。
关于 [TIMEOUT_CLEAN_QUEUE]broker busy 我们也可以适当调整 waitTimeMillsInSendQueue,默认值为200ms,可以适当调整到400ms。
如果大家觉得文章对您有帮助的话,麻烦帮忙点个赞,谢谢。
> 作者介绍:《RocketMQ技术内幕》作者,维护公众号:中间件兴趣圈,目前主要发表了源码阅读java集合、JUC(java并发包)、Netty、ElasticJob、Mycat、Dubbo、RocketMQ、mybaits等系列源码。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
比尔盖茨:反垄断案让我分心,不然微软定能打败安卓
据国外媒体报道,微软创始人比尔·盖茨(Bill Gates)表示,如果微软没有卷入美国司法部从1998年到2000年对其展开的反垄断调查,他认为现在每个人都会使用Windows Mobile操作系统。今天早些时候,盖茨在《纽约时报》举办的DealBook Conference上发表讲话,透露了他对微软智能手机操作系统所犯错误的看法。 盖茨声称:“毫无疑问,反垄断诉讼对微软不利,我们本该更专注于开发手机操作系统,这样的话今天你使用的将是Windows Mobile而不是Android。”“如果不是因为反垄断案让我分散了注意力,因此我搞砸了。” 微软从Windows Mobile到Windows Phone的混乱迁移让Android得以蓬勃发展,当时微软在移动领域拥有最大机会,但却放弃了。盖茨还透露,微软也错过了在关键的摩托罗拉手机上推出Windows Mobile操作系统的机会。 图示:Windows Phone 8.1 盖茨解释说:“我们只是晚了三个月才发布摩托罗拉会在手机上使用的操作系统版本,所以说,这是一个赢家通吃的游戏。”“现在这里没有人听说过Windows Mobile,但是就...
- 下一篇
Alibaba_Dragonwell OpenJDK VS OracleJDK 详细对比情况
导读:随着oracle各种疯狂版本的发布,Oracle JDK成为收费软件,很多公司转向寻找Oracle JDK的替代品。有很多人都会怀疑,Oracle JDK和其他JDK之间有没有很大的性能差异。加上笔者的1H2G1M的服务器,然后为了自己的技术能力的提升。本文作者通过不是很专业的方面测试,给出这个自认为可行的选择方案。 各大厂商频频推出自己的开源JDK是人性的扭曲还是道德的沦丧? 代码的有无国界是程序员的无奈还是大厂的爆发? 官放牛逼还是第三方牛逼? 官方为何变成版本发布狂? 服务器为何莫名宕机究竟是何人所为?内存一直占用率居高不下,究竟人为还是鬼祟? 官方与第三方的背后有隐藏着什么? 请关注今晚【开源oschina】的此篇文章, 测试环境:window10 测试容器:Tomcat8.5.47 测试JDK: 1、Alibaba_Dragonwell_8.1.1下载页面链接https://github.com/alibaba/dragonwell8/releases 2、官方jdk1.8.0_31 https://www.oracle.com/technetwork/java/j...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8编译安装MySQL8.0.19
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Hadoop3单机部署,实现最简伪集群
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果