案例分享|生产环境MQ集群一个非常诡异的消费延迟排查

news2024/11/26 22:39:23

1、问题现象

某一天,项目组一个同事向我反馈,他们使用公司的数据同步产品将MySQL数据同步到MQ集群,然后使用消费者将数据再同步到ES,反馈数据同步延迟严重,但对应的消费组确没有积压,但最近最近几分钟的数据都没有同步过来。

那问题来了,消费端没有消费积压,而且通过查看数据同步平台该通过任务的同步状态,同样显示没有积压,那是为什么呢?

遇到这个问题,我们应该冷静下来,分析一下其大概的数据流向图,梳理后如下图所示:

通过初步的诊断,从数据同步产品查看Binlog同步无延迟、MQ消费无积压,那为什么最终Es集群中的数据与MySQL有高达几分钟的延迟呢?

2、问题排查

根据上图几个关键组件数据同步延迟的检测,基本就排除了数据同步组件、MQ消费端本身消费的问题,问题的症结应该就是数据同步组件成功将数据写入到MQ集群,并且MQ集群返回了写入成功,但消费端并没有及时感知这个消息,也就是说消息虽然写入到MQ集群,但并没有达到消费队列。

因为如果数据同步组件如果没有写入成功,则MySQL Binlog日志就会出现延迟。但如果是MQ消费端的问题,则MQ平台也会显示消费组积压。

那为什么消息服务器写入成功,但消费组为什么感知不到呢?

首先为了验证上述结论是否正确,我还特意去看了一下主题的详细信息:

查看主题的统计信息时发现当前系统的时间为19:01分, 但主题最新的写入时间才是18:50,两者之间相差将近10分钟。

备注:上述界面是我们公司内部的消息运营管理平台,其实底层是调用了RocketMQ提供的topicStatus命令。

那这又是怎么造成的呢?

在这里我假设大家对RocketMQ底层的实现原理还不是特别熟悉,在这样的情况下,我觉得我们应该首先摸清楚topicStatus这个命令返回的minOffset、maxOffset以及lastUpdate这些是的具体获取逻辑,只有了解了这些,我们才能寻根究底,最终找到解决办法。

2.1 问题探究与原理剖析

在这个场景中,我们可以通过对topicStatus命令进行解析,从而探究其背后的实现原理。

当我们在命令行中输入 sh ./mqadmin topicStatus命令时,最终是调用defaultMQAdminExtImpl的examineTopicStats方法,最终在服务端的处理逻辑定义在AdminBrokerProcessor的getTopicStatsInfo方法中,核心代码如下:

这里的实现要点:

  • 通过MessageStore的getMinOffsetInQueue获取最小偏移量。

  • 通过MessageStore的getMaxOffsetInQueue获取最大偏移量。

  • 最新更新时间为最大偏移量减去一(表示最新一条消息)的存储时间

故要弄清队列最大、最小偏移量,关键是要看懂getMaxOffsetInQueue或者getMinOffsetInQueue的计算逻辑。

我也注意到分析源码虽然能直抵真相,但阅读起来太粗糙,所以我接下来的文章会尽量避免通篇的源码解读,取而代之的是只点出源码的入口处,其旁支细节将通过时序图获流程图,方便感兴趣的读者朋友去探究,我重点进行知识点的提炼,降低大家的学习成本。

如果大家想成体系的研究RocketMQ,想将消息中间件当成自己职业的闪光点,强烈建议购买我的两本关于RocketMQ的数据:《RocketMQ技术内幕》与《RocketMQ实战》。

MessageStore的getMaxOffsetInQueue的时序图如下所示:

从上述时序图我们可以得知,调用DefaultMessageStore的getMaxOffsetInQueue方法,首先是根据主题、队列ID获取ConsumeQueue对象(在RocketMQ中一个主题的一个队列会对应一个ConsumeQueue,代表一个消费队列),也就是这里获取的偏移量指的是消费者队列中的偏移量,而不是Commitlog文件的偏移量。

如果是找最大偏移量,就从该队列中的找到最后一个文件,去获取器最大的有效偏移量,也就是等于文件的起始偏移量(fileFromOffset)加上该文件当前最大可读的偏移量(readPosition),故引起这张时序图一个非常关键的点,就是如何获取消费队列最大的可读偏移量,代码见MappedFile的getReadPosition:

public int getReadPosition() {
   return this.writeBuffer == null ? this.wrotePosition.get() : this.committedPosition.get();
}

由于ConsumeQueue并没有 transientStorePoolEnable 机制,数据直接写入到FlieChannel中,故这里的writeBuffer为空,取的是 wrotePosition的值,那ConsumeQueue文件的wrotePosition值在什么地方更新呢?

这个可以通过查看MappedFile中修改wrotePosition的方法appendMessage方法的调用,如下图所示:

与ConsumeQueue对应的入口主要有两个:

  • ReputMessageService#doReput Commitlog异步转发线程,通过该线程异步构建Consumequeue、Index等文件

  • Commitlog#recoverAbnormally RocketMQ启动时根据Commitlog文件自动恢复Consumequeue文件

今天的主角当然不让非ReputMessageService莫属,这里先和大家普及一下一个最基本的知识:RocketMQ为了追求极致的顺序写,会将所有主题的消息顺序写入到一个文件(Commitlog文件),然后异步转发到ConsumeQueue(消费队列文件)、IndexFile(索引文件)。

其转发服务就是通过ReputMessageService来实现的。

在深入介绍Commitlog文件的转发机制之前,我在这里先问大家一个问题:消息是写入到内存就转发给ConsumeQueue,亦或是刷写到磁盘后再转发呢?

为了方便大家对这个问题的探究,其代码的核心入口如下图所示:

这里的关键实现要点如下:

  • 判断是否转发关键条件在于 isCommitlogAvailable()方法返回true

  • 根据转发位点reputFromOffset,从Commitlog文件中获取消息的物理偏移量、消息大小,tags等信息转发到消息消费队列、索引文件。

那isCommitlogAvailable的核心如下所示:

故转发的关键就在于Commitlog的maxOffset的获取逻辑了,其实现时序图如下所示:

da526050c232f4107f627eeeb9e6a083.png

这里核心重点是getReadPosition方法的实现,在RocketMQ写Commitlog文件,为了提升写入性能,引入了内存级读写分离机制,具体的实现原理如下图所示:

具体在实现层面,就是如果transientStorePoolEnable=true,数据写入到堆外内存(writeBuffer)中,然后再提交到FileChannel,提交的位置(commitedPosition来表示)。

大家可以分别看一下改变wrotePosition与committedPposition的调用链。

其中wrotePosition的调用链如下所示:

可以得知:wrotePosition是消息写入到内存(pagecache或者堆外内存)都会更新,但一旦开启了堆外内存机制,并不会取该值,所以我们可以理解为当消息写入到Pagecache中时,就可以被转发到消息消费队列。

紧接着我们再看一下committedPosition的调用链,如下所示:

原来在RocketMQ中,如果开启了transientStorePoolEnable机制,消息先写入到堆外内存,然后就会向消息发送者返回发送成功,然后会有一个异步线程(CommitRealTimeService)定时将消息(默认200ms一次循环)提交到FileChannel,即更新committedPosition的值,消息就会转发给消费队列,从而消费者就可以进行消费。

2.2 问题原因提炼

经过上面的解析,问题应该有所眉目了。

由于我们公司为了提高RocketMQ的资源利用率,提升RocketMQ的写入性能,我们开启了transientStorePoolEnable机制,消息发送端写入到堆外内存,就会返回写入成功,这样MySQL Binlog数据同步并不会产生延迟,那这里的问题,无非就2个:

  • CommitRealTimeService 线程并没有及时将堆外内存中的数据提交到FileChannel

  • ReputMessageService线程没有及时将数据转发到消费队列

由于目前我暂时对底层存储写入的原理还认识不够深入,对相关系统采集指标不够敏感,当时主要分析了一下线程栈,发现ReputMessageService线程一直在工作,推测可能是转发不及时,这块我还需要更加深入去研究,如果大家对这块有其实理解,欢迎留言,我也会在后续工作中提升这块的技能,更加深入去理解底层的原理。

也就是目前知道了问题的表象原因,虽然底层原理还未通透,但目前足以指导我们更好的处理问题:将集群内消息写入大的主题,迁移到其他负载较低的集群,从而降低该集群的写入压力,当迁移了几个主题后,果不其然,消息到达消费队列接近实时,集群得以恢复。

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

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

相关文章

LinkedIn领英开发客户方法大全(篇二)

profile完善步骤详细说明 详细步骤: 如下图箭头所示步骤,点击“view my profile”: 进入你的档案资料界面: 1)点击红色圆圈所示处,即进入Edit intro界面: 即编辑个人简介,包括: …

企业如何通过推特群推创造商机

尤其是小企业要足够重视Twitter在营销推广上的重要性。因此,你有必要精心设计Twitter主页,并付诸投入和努力,这样就会看到消费者数量的增长,以及品牌忠诚度的提升。无疑,在Twitter上,很多企业可以发现商机&…

HTML基础-表单标签,button按钮,select下拉菜单

HTML基础-表单标签 目标和学习路径 1.表单标签 1.1 input系列标签的基本介绍 1.2 input系列标签-文本框 (拓展)value属性和name属性作用介 1.3 input系列标签-密码框 1.4 input系列标签-单选框 1.5 input系列标签-复选框 1.6 input系列标签-文件选择 2.button按钮标签 1.7 inpu…

JVS无忧·企业计划2.1.6更新说明

无忧企业计划是JVS企业数字化全家桶中重要组成部分,主要用于项目管理、任务管理、进度跟踪、过程管理等场景。 任务管理是企业内部事务协同的重要工具,与传统的OA有类似之处,同样是推动企业内部事务有效快速的执行,但之前存在不同…

直流无刷电机(BLDC)转速闭环调速系统及Matlab/Simulink仿真分析(二)

文章目录前言一、转速闭环直流调速系统二、Matlab/Simulink仿真2.1.仿真电路分析2.2.仿真结果分析总结前言 变压调速是直流调速系统的主要调速方法,因此系统的硬件至少包含:可调直流电源和直流电机两部分。可调直流电源多采用直流PWM变换器,…

CMMI2.0之我见-过程质量保证PQA

编者按: CMMI2.0之我见系列将通过系列文章形式介绍CMMI2.0所涉及到的其中20个实践域,笔者将通过系统性的梳理、浅显易懂的文字描述,同时结合笔者的思考和观点,对每个实践域的目标以及所基本涵盖的内容进行描述,希望能…

CSS 的快乐:画一个可爱的三只小鸟 Button

做为前端工程师,最大的快乐之一就是可以用 CSS 画出各种有趣的效果。 比如我最近画的一个 Button: 画的过程中确实很开心,这也是我当时选择做前端的很大一部分原因。 今天我们就一起来画下这个可爱的 Button 吧!纯 CSS&#xff…

Kamiya丨Kamiya艾美捷小鼠血清淀粉样蛋白A ELISA说明书

Kamiya艾美捷小鼠血清淀粉样蛋白A ELISA预期用途: 小鼠血清淀粉样蛋白A ELISA是一种高灵敏度的双位点酶联免疫分析(ELISA)小鼠生物样品中血清淀粉样蛋白A(SAA)的定量测定。仅供研究使用。 引言 铜蓝蛋白是参与铜转运…

Go学习之路:更多类型:struct、slice 和映射(DAY 2)

文章目录前引更多类型:struct、slice 和映射1、指针2.1、结构体/结构体命名(一)2.2、结构体/对象访问、指针访问、初始化规则(二)3、数组4.1、切片/初始化切片4.2、切片/切片引用数组4.3、切片/切片的length和capacity…

React Context源码是怎么实现的呢

目前来看 Context 是一个非常强大但是很多时候不会直接使用的 api。大多数项目不会直接使用 createContext 然后向下面传递数据&#xff0c;而是采用第三方库&#xff08;react-redux&#xff09;。 想想项目中是不是经常会用到 connect(...)(Comp) 以及 <Provider value{s…

非互联网客户收入近6成,阿里云进入新周期

近日&#xff0c;阿里巴巴发布了截至2022年9月30日止季度&#xff08;2023财年第二季度&#xff0c;阿里财年从每年4月1日至第二年3月31日&#xff09;业绩。 在除去阿里内部使用的额度&#xff0c;抵销跨分部交易后&#xff0c;阿里云业务分部&#xff08;包括阿里云和钉钉&am…

【web前端期末大作业】基于html+css+javascript+jquery技术设计的音乐网站(44页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

【华为上机真题 2022】字符串加密

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

【Linux】基本指令(三)

文章目录sort 指令find 指令which 指令alias 指令grep 指令top 指令zip/unzip指令tar 指令bc 指令unname 指令history 指令shutdown 指令热键补充sort 指令 &#x1f495; Linux sort 命令用于将文本文件内容加以排序并且可针对文本文件的内容&#xff0c;以行为单位来排序。 …

03 探究Kubernetes工作机制的奥秘

自动化的运维管理&#xff1a;探究Kubernetes工作机制的奥秘 文章目录1. 云计算时代的操作系统2. kubernets 的基本架构3.节点内部的结构3.1 master 节点3.2 node 节点4. Kubernetes 的大致工作流程5.插件有哪些&#xff1f;5.1 重要的插件6. kubernetes 架构思维导图7. 思考的…

制作一个简单HTML校园网页(HTML+CSS)学校网站制作 校园网站设计与实现

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

RabbitMQ工作队列

工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务&#xff0c;而不得不等待它完成。 相反我们安排任务在之后执行。我们把任务封装为消息并将其发送到队列。在后台运行的工作进程将弹出任务并最终执行作业。当有多个工作线程时&#xff0c;这些工作线程将一起处…

电商购物平台的不断完善

目录 1.java编译环境的创建&#xff0c;与所需要用到的插件 第一个电商平台 1.初步思路&#xff1a; 2.确定java类 3.源码分析 成果&#xff1a; 第二个电商购物平台 代码&#xff1a; 最终的成果&#xff1a; 1.java编译环境的创建&#xff0c;与所需要用到的插件 (1…

链夹式烟苗注水移栽机的总体设计

目 录 1 引言 1 1.1课题来源及研究的目的和意义 1 1.2农艺要求 1 1.3链夹式烟苗注水移栽机的发展现状 1 1.4研究内容 3 2 链夹式烟苗注水移栽机的设计方案 4 2.1链夹式烟苗注水移栽机总体方案设计思路 4 2.2链夹式烟苗注水移栽机工作原理 5 3 链夹式烟苗注水移栽机具体设计 6 3…

带你入门HTML+CSS网页设计,编写网页代码的思路

带你入门HTMLCSS网页设计&#xff0c;编写网页代码的思路 这篇文章主要给大家详细解释一下这些代码的作用和意义&#xff0c;以及编写网页代码的格式与思路。 下面我贴上html代码&#xff1a; <!--HTML--> <div> <h2>这是我的第一个网页</h2> <p&…