RocketMQ一行代码造成消息发送失败

这是我的第 198 期分享

作者 | 丁威

来源 | 中间件兴趣圈(ID:dingwpmz_zjj)

分享 | Java中文社群(ID:javacn666)

1、问题现象


首先接到项目反馈使用 RocketMQ 会出现如下错误:

错误信息关键点:MQBrokerException:CODE:2 DESC:[TIMEOUT_CLEAN_QUEUE]broker busy,start flow control for a while,period in queue:205ms,size of queue:880。

由于项目组并没有对消息发送失败做任何补偿,导致丢失消息发送失败,故需要对这个问题进行深层次的探讨,并加以解决。

2、问题分析


首先我们根据关键字:TIMEOUT_CLEAN_QUEUE 去 RocketMQ 中查询,去探究在什么时候会抛出如上错误。根据全文搜索如下图所示:

该方法是在 BrokerFastFailure 中定义的,通过名称即可以看成其设计目的:Broker端快速失败机制。

Broker 端快速失败其原理图如下:


  • 消息发送者向 Broker 发送消息写入请求,Broker 端在接收到请求后会首先放入一个队列中(SendThreadPoolQueue),默认容量为 10000。

  • Broker 会专门使用一个线程池(SendMessageExecutor)去从队列中获取任务并执行消息写入请求,为了保证消息的顺序处理,该线程池默认线程个数为1。

如果 Broker 端受到垃圾回收等等因素造成单条写入数据发生抖动,单个 Broker 端积压的请求太多从而得不到及时处理,会极大的造成客户端消息发送的时间延长。

设想一下,如果由于 Broker 压力增大,写入一条消息需要500ms甚至超过1s,并且队列中积压了5000条消息,消息发送端的默认超时时间为3s,如果按照这样的速度,这些请求在轮到 Broker 执行写入请求时,客户端已经将这个请求超时了,这样不仅会造成大量的无效处理,还会导致客户端发送超时。

故 RocketMQ 为了解决该问题,引入 Broker 端快速失败机制,即开启一个定时调度线程,每隔10毫秒去检查队列中的第一个排队节点,如果该节点的排队时间已经超过了 200ms,就会取消该队列中所有已超过 200ms 的请求,立即向客户端返回失败,这样客户端能尽快进行重试,因为 Broker 都是集群部署,下次重试可以发送到其他 Broker 上,这样能最大程度保证消息发送在默认 3s 的时间内经过重试机制,能有效避免某一台 Broker 由于瞬时压力大而造成的消息发送不可用,从而实现消息发送的高可用。

从 Broker 端快速失败机制引入的初衷来看,快速失败后会发起重试,除非同一时刻集群内所有的 Broker 都繁忙,不然消息会发送成功,用户是不会感知这个错误的,那为什么用户感知了呢?难道 TIMEOUT_ CLEAN _ QUEUE 错误,Broker 不重试?

为了解开这个谜团,接下来会采用源码分析的手段去探究真相。接下来将以消息同步发送为例揭示其消息发送处理流程中的核心关键点。

MQ Client 消息发送端首先会利用网络通道将请求发送到 Broker,然后接收到请求结果后并调用 processSendResponse 方法对响应结果进行解析,如下图所示:

在这里返回的 code 为 RemotingSysResponseCode . SYSTEM_BUSY。

我们从 proccessSendResponse 方法中可以得知如果 code 为 SYSTEM_BUSY,该方法会抛出 MQBrokerException,响应 code 为 SYSTEM_BUSY,其错误描述为开头部分的错误信息。

那我们沿着该方法的调用链路,可以找到其直接调用方:DefaultMQProducerImpl 的 sendKernelImpl,我们重点考虑如果底层方法抛出  MQBrokerException 该方法会如何处理。

其关键代码如下图所示:

可以看出在 sendKernelImpl 方法中首先会捕捉异常,先执行注册的钩子函数,即就算执行失败,对应的消息发送后置钩子函数也会执行,然后再原封不动的将该异常向上抛出。

sendKernelImpl 方法被 DefaultMQProducerImpl 的 sendDefaultImpl 方法调用,下面是其核心实现截图:

从这里可以看出 RocketMQ 消息发送高可用设计一个非常关键的点,重试机制,其实现是在 for 循环中 使用 try catch 将 sendKernelImpl 方法包裹,就可以保证该方法抛出异常后能继续重试。从上文可知,如果 SYSTEM_BUSY 会抛出 MQBrokerException,但发现只有上述几个错误码才会重试,因为如果不是上述错误码,会继续向外抛出异常,此时 for 循环会被中断,即不会重试。

这里非常令人意外的是连 SYSTEM_ERROR 都会重试,却没有包含 SYSTEM_BUSY,显然违背了快速失败的设计初衷,故笔者断定,这是 RocketMQ 的一个BUG,将 SYSTEM_BUSY 遗漏了,后续会提一个 PR,增加一行代码,将 SYSTEM_BUSY 加上即可。

问题分析到这里,该问题应该就非常明了。

3、解决方案


如果大家在网上搜索 TIMEOUT_CLEAN_QUEUE 的解决方法,大家不约而同提出的解决方案是增加 waitTimeMillsInSendQueue 的值,该值默认为 200ms,例如将其设置为 1000s 等等,以前我是反对的,因为我的认知里 Broker 会重试,但现在发现 Broker 不会重试,所以我现在认为该 BUG未解决的情况下适当提高该值能有效的缓解。

但这是并不是好的解决方案,我会在近期向官方提交一个PR,将这个问题修复,建议大家在公司尽量对自己使用的版本进行修改,重新打一个包即可,因为这已经违背了 Broker 端快速失败的设计初衷。

但在消息发送的业务方,尽量自己实现消息的重试机制,即不依赖 RocketMQ 本身提供的重试机制,因为受制于网络等因素,消息发送不可能百分之百成功,建议大家在消息发送时捕获一下异常,如果发送失败,可以将消息存入数据库,再结合定时任务对消息进行重试,尽最大程度保证消息不丢失。

END

Redis的自白:我为什么在单线程的这条路上越走越远?

人人都能看懂的 6 种限流实现方案!(纯干货)

关注公众号发送”进群“,磊哥拉你进读者群。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/546129.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 中的 String 有没有长度限制?

这是我的第 199 期分享作者 | Hollis来源 | Hollis(ID:hollischuang) 分享 | Java中文社群(ID:javacn666)关于String有没有长度限制的问题,我之前单独写过一篇文章分析过,最近我又抽…

开源 免费 java CMS - FreeCMS2.1 菜单管理

2019独角兽企业重金招聘Python工程师标准>>> 项目地址:http://www.freeteam.cn/ 菜单管理 FreeCMS在设计时定位于面向二次开发友好,所以FreeCMS提供了菜单管理功能,二次开发人员可以自由增加新的功能菜单到FreeCMS。 为了让后台…

本来想用“{{”秀一波,结果却导致了内存溢出!

这是我的第 200 期分享作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)生活中的尴尬无处不在,有时候你只是想简单的装一把,但某些“老同志”总是在不…

局部变量竟然比全局变量快 5 倍?

这是我的第 201 期分享作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)喽,大家好,磊哥的性能优化篇又来了!其实写这个性能优化类的文章初…

FreeMarker笔记 前言第1章 入门

简介 简介 FreeMarker是一款模板引擎:一种基于模板的、用来生成输出文本(任何来自于HTML格式的文本用来自动生成源代码)的通用工具。它是为Java程序员提供的一个开发包或者说是类库。它不是面向最终用户,而是为程序员提供的可以嵌…

厉害了,3万字的MySQL精华总结 + 面试100问!

这是我的第 202 期分享作者 | 派大新来源 | JavaKeeper(ID:JavaKeeper)分享 | Java中文社群(ID:javacn666)❝写在之前:不建议那种上来就是各种面试题罗列,然后背书式的去记忆&#x…

网页视频播放器代码大全 + 21个为您的站点和博客提供的免费视频播放器

推荐 使用 极酷 Web在线播放器。网页中嵌入视频代码综合全然版 1.avi格式 代码片断例如以下&#xff1a;  程序代码 <objectid"video"width"400"height"200"border"0"classid"clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA&q…

漫话:为什么计算机起始时间是1970年1月1日?

这是我的第 203 期分享作者 | 漫画编程来源 | 漫画编程&#xff08;ID&#xff1a;mhcoding&#xff09;分享 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;问题复现1970-01-01对于开发者来说都是不陌生的&#xff0c;有些系统对于时间的处理如果不够好的话&…

puppeteer执行js_使用Node.js和Puppeteer与表单和网页进行交互– 1

puppeteer执行jsHi guys! Today lets look at another powerful function of the puppeteer API using Node.js. 嗨&#xff0c;大家好&#xff01; 今天&#xff0c;让我们看看使用Node.js的puppeteer API的另一个强大功能。 In the first part of this section, lets look a…

面试官:不会看SQL执行计划,简历也敢写精通SQL优化?

这是我的第 204 期分享作者 | 程序员内点事来源 | 程序员内点事&#xff08;ID&#xff1a;chengxy-nds&#xff09;分享 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;昨天中午在食堂&#xff0c;和部门的技术大牛们坐在一桌吃饭&#xff0c;作为一个卑微技…

scrollTop的兼容性小结

2019独角兽企业重金招聘Python工程师标准>>> 在页面上加上了 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 之后&#xff0c;document.body.scrollTop的值…

阿里巴巴为什么让初始化集合时必须指定大小?

这是我的第 205 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;哈喽&#xff0c;亲爱的小伙伴们&#xff0c;技术学磊哥&#xff0c;进步没得说&#xff01;欢迎来到…

ios页面间跳转方式总结

转自&#xff1a;http://www.cnblogs.com/anywherego/p/3542202.html 下面以OldViewController(oldC)的按钮btn点击后跳转到NewViewController(newC)为例说明: 1.Storyboard的segues方式 鼠标点击按钮btn然后按住control键拖拽到newC页面&#xff0c;在弹出的segue页面中选择跳…

啪啪打脸!领导说:try-catch要放在循环体外!

这是我的第 206 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;哈喽&#xff0c;亲爱的小伙伴们&#xff0c;技术学磊哥&#xff0c;进步没得说&#xff01;欢迎来到…

软件项目与产品的区别与联系_软件产品和软件过程之间的区别和关系

软件项目与产品的区别与联系软件产品和软件过程 (Software product and Software process) Software product and Software process: These two words are the one which is mostly confused with each other. In this article, we are going to explain each of these in deta…

Oracle官方推荐的性能测试工具!简单、精准又直观!

这是我的第 207 期分享作者 | 武培轩来源 | 武培轩&#xff08;ID&#xff1a;wupeixuan404&#xff09;分享 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;磊哥在前面的所有性能优化的文章中都是用了这款性能测试工具——JMH&#xff0c;一直没来得及给大家…

Hadoop开发第2期---虚拟机中搭建Linux

注&#xff1a;关于如何将hadoop源码导入Eclipse详见http://pan.baidu.com/s/1hq8ArUs 一、Hadoop配置软件&#xff08;我的电脑是Windows7旗舰--64bit&#xff09; 1. VMWare专用CentOS镜像(Centos是Linux操作系统的一种)2. VMware-workstation103. hadoop-1.1.2.tar.gz4. jdk…

看故事学知识,这篇Java代理的文章妙啊!

这是我的第 208 期分享作者 | java金融来源 | java金融&#xff08;ID&#xff1a;java4299&#xff09;分享 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;什么是代理代理模式是常用的java设计模式&#xff0c;他的特征是代理类与委托类有同样的接口&#x…

阿里《Java开发手册》中的 1 个bug!

这是我的第 210 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;本来打算写一篇《阿里巴巴为什么不允许日志输出时&#xff0c;使用字符串拼接&#xff1f;》的文章&a…

驳《阿里「Java开发手册」中的1个bug》?

这是我的第 211 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;前两天写了一篇关于《阿里Java开发手册中的 1 个bug》的文章&#xff0c;评论区有点炸锅了&#xff0…