rocketmq 发送时异常:system busy 和 broker busy 解决方案(2019-06更新)
推荐阅读:https://juejin.im/post/5d996285f265da5bad40523c
这个文章从源码上分析了原因,并给出了解决方案。
文章解决方案一:在broker.config中将transientStorePoolEnable=true。
文章解决方案二:扩容broker服务器。
具体解决方案说明,可自行阅读文章。
------------------------以下为原内容--------------------------------------
之前写的解决方案,都是基于测试环境测试的.到生产环境之后,正常使用没有问题,生产环境压测时,又出现了system busy异常(简直崩溃).最后在rocketmq群里大佬指导下,终于解决(希望是彻底解决).
下面直接给出结果:
目前通过生产环境各种参数修改测试得出:
broker busy异常: 可通过增大 waitTimeMillsInSendQueue 解决
system busy异常:可通过增大 osPageCacheBusyTimeOutMills 解决
#发送队列等待时间 waitTimeMillsInSendQueue=3000 #系统页面缓存繁忙超时时间(翻译),默认值 1000 osPageCacheBusyTimeOutMills=5000
个人猜测,出现异常的原因是因为我们同一台服务器部署的多个应用造成的.我们一台服务器上部署了 三个ES、八个redis、一个rocketmq ,压力测试时这些都在使用,虽然cpu、内存都还有很大剩余,但是磁盘io和内存频率毕竟只有那么多可能已经占满,或者还有其他都会有影响。
之前测试环境测试其他东西时,发现mq和redis同时大量使用时,redis速度会降低三到四倍,由此可见应用分服务器部署的重要性。以前知道会有影响,没想到影响这么大。
最终结解决方案:应该给rocketmq单独部署性能较高的服务器.
下面给下我们完整的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #broker名字,注意此处不同的配置文件填写的不一样 brokerClusterName=rocketmqcluster brokerName=broker-a #0 表示 Master, >0 表示 Slave brokerId=0 #nameServer地址,分号分割 namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876 #这个配置可解决双网卡,发送消息走外网的问题,这里配上内网ip就可以了 brokerIP1=10.30.51.149 #在发送消息时,自动创建服务器不存在的topic,默认创建的队列数 defaultTopicQueueNums=8 #是否允许 Broker 自动创建Topic,建议线下开启,线上关闭 autoCreateTopicEnable= false #是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭 autoCreateSubscriptionGroup= true #Broker 对外服务的监听端口 listenPort=10911 #删除文件时间点,默认凌晨 0点 deleteWhen=03 #文件保留时间,默认 48 小时 fileReservedTime=48 #commitLog每个文件的大小默认1G mapedFileSizeCommitLog=1073741824 #ConsumeQueue每个文件默认存30W条,根据业务情况调整 mapedFileSizeConsumeQueue=1000000 destroyMapedFileIntervalForcibly=120000 redeleteHangedFileInterval=120000 #检测物理文件磁盘空间 diskMaxUsedSpaceRatio=88 #存储路径 storePathRootDir=/app/data/rocketmq/data #commitLog 存储路径 storePathCommitLog=/app/data/rocketmq/data/commitlog #消费队列存储路径存储路径 storePathConsumeQueue=/app/data/rocketmq/data/consumerqueue #消息索引存储路径 storePathIndex=/app/data/rocketmq/data/index #checkpoint 文件存储路径 storeCheckpoint=/app/data/rocketmq/data/checkpoint #abort 文件存储路径 abortFile=/app/data/rocketmq/data/abort #限制的消息大小 修改为16M maxMessageSize=16777216 #发送队列等待时间 waitTimeMillsInSendQueue=3000 osPageCacheBusyTimeOutMills=5000 flushCommitLogLeastPages=12 flushConsumeQueueLeastPages=6 flushCommitLogThoroughInterval=30000 flushConsumeQueueThoroughInterval=180000 #Broker 的角色 #- ASYNC_MASTER 异步复制Master #- SYNC_MASTER 同步双写Master #- SLAVE brokerRole=ASYNC_MASTER #刷盘方式 #- ASYNC_FLUSH 异步刷盘 #- SYNC_FLUSH 同步刷盘 flushDiskType=ASYNC_FLUSH #checkTransactionMessageEnable=false #发消息线程池数量 sendMessageThreadPoolNums=80 #拉消息线程池数量 pullMessageThreadPoolNums=128 useReentrantLockWhenPutMessage= true |
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 上面是最新更新 2019-06-13 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
记一次 rocketmq 使用时的异常。
这里就不说什么rocketmq 源码啥的了,因为没看过。网上一搜这两个异常 大部分都是什么源码解读,也没说出现后的解决办法(蓝瘦香菇)。
大量测试发现:
1、system busy , start flow control for a while
该异常会造成 消息丢失。
2、broker busy , start flow control for a while
该异常不会造成消息丢失。(这是最坑的,都异常了消息竟然是正常发送了的。)
解决过程:
1、最开始时候 ,测试发现在性能好的服务器上 只会出现system busy,也就是说出现异常就会消息丢失。
所以:业务代码进行处理,出现异常就会重发到当前topic的 bak队列,当时想的是既然这个topic busy了,就换到另外的topic去发,总不能都 busy吧。
也算是临时解决了。
2、运行一年后,可能是服务器上运行的东西多了,或者其他原因。发现有消息重复的现象。不用想肯定是报broker busy异常,重发到topic的 bak队列了。又因为broker busy可能不会造成消息丢失,所以消息重复就出现了。
3、无奈,找新的解决方法。本来想的是判断异常,如果是broker busy就不重发了。
报着试一试的态度,又去百度了一下,还是搜出来一堆源码解读。搭上梯子,google一下,还真找到了。
https://stackoverflow.com/questions/47749906/rocketmq-throw-exception-timeout-clean-queuebroker-busy-start-flow-control-f
https://www.cnblogs.com/cs99lzzs/p/9181555.html
想到不知道在哪看的的一句话,在stackoverflow上能找到和你一样的问题,那问题已经解决了百分之90了。这他喵的真实至理名言啊。
==============吐槽完=================
又经过大量测试验证:
解决方案:
修改rocketmq配置文件:
方案一:sendMessageThreadPoolNums 改成 1 ,没有的话新增一行。
sendMessageThreadPoolNums=1
方案二(推荐):useReentrantLockWhenPutMessage改成true,没有的话新增一行。
sendMessageThreadPoolNums=32
useReentrantLockWhenPutMessage=true
说明:
sendMessageThreadPoolNums这个属性是发送线程池大小, rocketmq4.1版本之后默认为 1,之前版本默认什么不知道但是肯定大于1。这个属性改成1的话,就不用管useReentrantLockWhenPutMessage这个属性了;
如果改成大于1,就需要将useReentrantLockWhenPutMessage这个属性设置为 true;
目前测试 未发现这两个方案有什么区别,sendMessageThreadPoolNums=1 时也支持多线程发送,发送速度感觉和 sendMessageThreadPoolNums大于1没有区别,都能跑满100M的网卡。
感觉如果useReentrantLockWhenPutMessage=true的时候,就是打开锁(属性名翻译一下也大概是这个意思),然后关键代码其实还是单线程处理;
有闲功夫的话去翻翻源码看看去。
最后 我是选择的方案二,毕竟看着好看点。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步