深入剖析:如何使用Pulsar和Arthas高效排查消息队列延迟问题
背景
前两天收到业务反馈有一个 topic 的分区消息堆积了:

根据之前的经验来看,要么是业务消费逻辑出现问题导致消费过慢,当然也有小概率是消息队列的 Bug(我们使用的是 pulsar)。
排查

通过排查,发现确实是在一点多的时候消息堆积了(后面是修复之后堆积开始下降)。
于是我在刚才堆积处查看了一条堆积消息的列表:

获取到其中一条消息的 messageId.
这里本质上使用的是 pulsar-admin 的 API。
org.apache.pulsar.client.admin.Topics#peekMessages

再通过这条消息的 id (为了演示,这里的 messageId 可能不一样)在我们的 pulsar 消息链路系统中找到了消息的发送链路:

通过这个链路会发现消息一直在推送,但就是没有收到客户端的 ACK 响应。
相关的消息链路埋点可以参考这里:如何编写一个 Pulsar Broker Interceptor 插件
简单来说就是在以下几个 broker 提供的拦截器接口加上埋点数据即可:
- messageProduced
- messageDispatched
- messageAcked
既然知道了是客户端没有响应 ACK,那就得知道客户端此时在干什么。
首先排查了 JVM 内存、CPU 等监控情况,发现一切都挺正常的,这段时间没有明显的尖刺。
Arthas 排查
于是便准备使用 arthas 查看下线程的运行情况。
我们进入到对应 Pod 的容器,执行:
java -jar arthas-boot.jar
因为 JVM 内存都没有啥异常,所以先看看 thread 的运行堆栈,考虑到是 pulsar 消费线程卡住了,所以我们需要加上线程状态已经过滤下线程的名称:

thread --state WAITING | grep pulsar
此时就会列出当前 Java 进程中状态为 WATING 并且线程名称以 pulsar 开头的线程。
我在之前的文章 从 Pulsar Client 的原理到它的监控面板 中分析过客户端的原理。


可以知道 pulsar 客户端在其中使用的是 pulsar-打头的线程名称,所以这样就列出了我们需要重点关注的线程。
我们以图中列出的线程 Id:320 为例:
thread 320

此时便会打印当前线程的堆栈。
从上述堆栈中会发现线程一直处于 IO 操作中,看起来是在操作数据库。
我们再往下翻一翻,会发现上层调用的业务代码:

查阅代码得知这是一个数据库的写入操作,看起来是在这个环节数据库响应过慢导致的 pulsar 线程被阻塞了;从而导致消息没有及时 ACK。
为了最终确认是否由数据库引起的,于是继续查询了当前应用的慢 SQL 情况:

发现其中有一个查询语句调用频次和平均耗时都比较高,而且正好这个表也是刚才在堆栈里操作的那张表。
经过业务排查发现这个慢 SQL 是由一个定时任务触发的,而这个定时任务由于某些原因一直也没有停止,所以为了快速解决这个问题,我们先尝试将这个定时任务停掉。
果然停掉没多久后消息就开始快速消费了:

从这个时间线也可以看得出来了,在服务端推送了多次之后终于收到了 ACK。
修复之后业务再去排查优化这个慢 SQL,这样这个问题就得到根本的解决了。
更多好用技巧
当然 arthas 好用的功能还远不止此,我觉得还有以下功能比较好用:
火焰图
profile:可以输出火焰图,在做性能分析的时候非常有用。

动态修改内存数据
还记得之前我们碰到过一个 pulsar 删除 topic 的 Bug,虽然最终修复了问题,但是在发布修复版本的时候为了避免再次触发老版本的 bug,需要在内存中将某个关键字段的值修改掉。
而且是不能重启应用的情况下修改,此时使用 arthas 就非常的方便:
curl -O https://arthas.aliyun.com/arthas-boot.jar && java -jar arthas-boot.jar 1 -c "vmtool -x 3 --action getInstances --className org.apache.pulsar.broker.ServiceConfiguration --express 'instances[0].setTopicLevelPoliciesEnabled(false)'"
这里使用的是 vmtool 这个子命令来获取对象,最终再使用 express 表达式将其中的值改为了 false。
当然这是一个高危操作,不到万不得已不推荐这么使用。
Arthas Tunnel & Web Console
这是一个方便开发者通过网页就可以连接到 arthas 的功能,避免直接登录到服务器进行操作。


我们在研效普通也内置了该功能,让开发排查问题更加方便。
CPU 使用过多
cpu 异常使用排查也是一个非常有用的功能,虽然我们可以通过监控得知 JVM 的 cpu 使用情况,但是没法知道具体是哪个线程以及哪行代码造成的 cpu 过高。
thread -n 3

使用以上命令就可以将 cpu 排名前三的线程打印出来,并且列出他的堆栈情况,这样可以很直观的得知 cpu 消耗了在哪些地方了。
当然还有一些 trace 查询:
trace demo.MathGame run '#cost > 10'
比如这是将调用超过 10ms 的函数打印出来,不过如果我们接入了可观测系统(OpenTelemetry、skywalking等)这个功能就用不太上了。
还可以在运行的时候不停机修改日志级别,这种在线上排查一些疑难杂症的时候非常好用(通常情况下 debug 日志是不打印的),我们可以将日志级别调整为 debug 打印出更加详细的信息:
[arthas@2062]$ logger --name ROOT --level debug
update logger level success.
如果是在 kubernetes 环境中执行也有可能碰到 Java 进程启动后没有在磁盘中写入 PID 的情况:
$ java -jar arthas-boot.jar
[INFO] arthas-boot version: 3.6.7
[INFO] Can not find java process. Try to pass <pid> in command line.
Please select an available pid.
导致直接运行的时候无法找到 Java 进程;此时就需要先 ps 拿到 PID 之后再传入 PID 连入 arthas:
$ java -jar arthas-boot.jar 1
更多关于 arthas 的用法可以参考官网。
参考链接:
- https://pulsar.apache.org/docs/3.2.x/admin-api-topics/#peek-messages
- https://crossoverjie.top/2023/12/11/ob/Pulsar-Broker-Interceptor/
- https://arthas.aliyun.com/
- https://crossoverjie.top/2024/01/09/ob/Pulsar-Delete-Topic/
深入剖析:如何使用Pulsar和Arthas高效排查消息队列延迟问题的更多相关文章
- RabbitMQ和Kafka,更加便捷高效的消息队列使用方式,请放心食用
一.RabbitMQ实例介绍RabbitMQ实例由华为云分布式消息服务(DMS)团队打造,实例采用物理隔离的方式部署,租户独占RabbitMQ实例.一键式部署,完全兼容开源RabbitMQ的使用方式, ...
- 深入剖析 RabbitMQ —— Spring 框架下实现 AMQP 高级消息队列协议
前言 消息队列在现今数据量超大,并发量超高的系统中是十分常用的.本文将会对现时最常用到的几款消息队列框架 ActiveMQ.RabbitMQ.Kafka 进行分析对比.详细介绍 RabbitMQ 在 ...
- 分布式消息队列Apache Pulsar
Pulsar简介 Apache Pulsar是一个企业级的分布式消息系统,最初由Yahoo开发并在2016年开源,目前正在Apache基金会下孵化.Plusar已经在Yahoo的生产环境使用了三年多, ...
- 老李推荐:第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列
老李推荐:第6章6节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令队列 事件源在获得字串命令并把它翻译成对应的MonkeyEvent事件后,会把这些 ...
- laravel的消息队列剖析
laravel的消息队列剖析 这篇来自于看到朋友转的58沈剑的一篇文章:1分钟实现"延迟消息"功能 在实际工作中也不止遇见过一次这个问题,我在想着以前是怎么处理的呢?我记得当初在上 ...
- 剖析nsq消息队列(一) 简介及去中心化实现原理
分布式消息队列nsq,简单易用,去中心化的设计使nsq更健壮,nsq充分利用了go语言的goroutine和channel来实现的消息处理,代码量也不大,读不了多久就没了.后期的文章我会把nsq的源码 ...
- 剖析nsq消息队列(二) 去中心化代码源码解析
在上一篇帖子剖析nsq消息队列(一) 简介及去中心化实现原理中,我介绍了nsq的两种使用方式,一种是直接连接,还有一种是通过nslookup来实现去中心化的方式使用,并大概说了一下实现原理,没有什么难 ...
- 剖析nsq消息队列(四) 消息的负载处理
剖析nsq消息队列-目录 实际应用中,一部分服务集群可能会同时订阅同一个topic,并且处于同一个channel下.当nsqd有消息需要发送给订阅客户端去处理时,发给哪个客户端是需要考虑的,也就是我要 ...
- 剖析nsq消息队列目录
剖析nsq消息队列(一) 简介及去中心化实现原理 剖析nsq消息队列(二) 去中心化源码解析 剖析nsq消息队列(三) 消息传输的可靠性和持久化[一] 剖析nsq消息队列(三) 消息传输的可靠性和持久 ...
- Java诊断利器Arthas优雅排查生产环境
前言 Arthas 是Alibaba开源的Java诊断工具.在线排查问题,无需重启:动态跟踪Java代码:实时监控JVM状态.对分秒必争的线上异常,Arthas可帮助我们快速诊断相关问题. 下载安装 ...
随机推荐
- vivado的VIO调试工具的使用
vivado中的VIO调试工具的使用 1.实验原理 前面一篇介绍了ILA的独立测试,vivado中还有其他的FPGA测试工具.其中VIO就是个比较常用的工具.相对于ILA更多的关注波形,VIO则专注于 ...
- matlab学习系列
matlab系列学习 1.学习缘由 本来已经学习过这个软件,了解了包括电路仿真在内的诸多功能,能够比较熟练地编写m文件和函数. 但是,在最近的依次练习中发现之前的许多操作都忘记了.有一些基本的语法都不 ...
- linux系统执行 ifconfig命令ens33没有显示ip地址解决办法
问题背景 安装虚拟机(以ubuntu为例)因为重启或更新等原因导致,无法通过ssh连接到虚拟机,本地cmd窗口ping虚拟机ip发现无法ping通 启动本地虚拟机 发现ens33 位置没有显示 ip地 ...
- archlinux xfce未中文化 goldendict不能显示中文
下载个中文字体包就好了 https://wiki.archlinuxcn.org/wiki/简体中文本地化
- Flume入门操作
十一.Flume 1)开启Flume的监控端口 bin/flume-ng agent -c conf/ -n a1 -f job/flume-netcat-logger.conf -Dflume.ro ...
- 美团一面,面试官让介绍AQS原理并手写一个同步器,直接凉了
写在开头 今天在牛客上看到了一个帖子,一个网友吐槽美团一面上来就让手撕同步器,没整出来,结果面试直接凉凉. 就此联想到一周前写的一篇关于AQS知识点解析的博文,当时也曾埋下伏笔说后面会根据AQS的原理 ...
- HarmonyOS音频开发指导:使用AudioRenderer开发音频播放功能
AudioRenderer是音频渲染器,用于播放PCM(Pulse Code Modulation)音频数据,相比AVPlayer而言,可以在输入前添加数据预处理,更适合有音频开发经验的开发者,以 ...
- C++对象封装后的内存布局
在C语言中,数据和数据的处理操作(函数)是分开声明的,在语言层面并没有支持数据和函数的内在关联性,我们称之为过程式编程范式或者程序性编程范式.C++兼容了C语言,当然也支持这种编程范式.但C++更主要 ...
- RestfulApi 学习笔记——分页和排序(六)
前言 分页和排序时一些非常常规的操作,同样也有一些我们注意的点. 正文 分页 先来谈及分页. 看下前端传递的参数. public class EmployeeDtoParameters { priva ...
- Python中两种网络编程方式:Socket和HTTP协议
本文分享自华为云社区<Python网络编程实践从Socket到HTTP协议的探索与实现>,作者:柠檬味拥抱. 在当今互联网时代,网络编程是程序员不可或缺的一项技能.Python作为一种高级 ...