记一次线程堵塞(挂起)导致消息队列积压

1 背景

A服务作为生产者,每天发送上千万的mq消息,每一个消息包含500个用户ids数据。B服务作为消费者,接受MQ消息并通过http调用第三方请求进行业务处理,消费组启用了rabbitmq的多线程消费组,一个实例并发40个mq消费者线程,每个线程一次获取10个消息进行消费。

Mq消费者配置如下:

# mq配置rabbitmq:connection-timeout: 15000cache:channel:size: 200# 消息发送到rabbitmq broker cluster需要回调publisher-confirms: true# 交换机将消息投递至队列失败时需要回调publisher-returns: truelistener:# 手动确认消息已被消费simple:acknowledge-mode: manual# consumer的并发数concurrency: 40max-concurrency: 50# 每个消息者每次取10条prefetch: 10

Mq挤压消息如下

2 排查

2.1 复制rabbitmq挤压消息数据进行模拟复现

找出rabbitmq挤压的消息,在本地模拟消费,找出没有进行消息确认的原因,通过rabbitmq控制台的Get messages功能

复制payload的消息进行base64转码,转出来的消息是乱码不完整的,怀疑
是rabbitmq还结合了其他加密处理,放弃这种排查思路

2.2 检查报错日志

rabbitmq的unack消息挤压,那就是消费者没有进行ack确认,怀疑消费者代码有异常导致没能执行到ack的代码。
查询服务器日志,没发现有报错的日志,梳理业务代码,消费者使用了spring aop around机制进行消息确认,所以不管代码有没有报错,按理说都会手动进行mq消息ack确认

2.3 检查服务是否宕机

消费组实例数量符合服务器大小配置,因此服务器应用没有宕机

2.4 检查java线程

使用IBM的TMDA工具进行分析线程堆栈,工具下载地址
TMDA工具下载地址

TMDA工具简介

TMDA分析线程堆栈结果如下

通过分析图,看到大量park线程,确实是符合现状,应用的线程挂起了

3 分析和解决

通过stack深度高到底排序,业务代码存在线程等待情况,具体代码CountDownLatch.await

3.1 结合业务代码分析

通过上图stack提示,找到关联的业务代码

伪代码如下:

// new一个CompletableFuture
public CompletableFuture<Integer> httpCall(String tokenData){CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {long time = 3000L;try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}return Integer.parseInt(tokenData);});return completableFuture;}httpCall(tokenData).whenCompleteAsync((returnValue, ex)->{// do business// ex.getMessage()// 其中ex对象为空,使用ex.getMessage()报了空指针,导致没能执行如下的countDowncountDownLatch.countDown();})

消费者服务通过http调用第三方服务,为了提高并发,使用了多线程,每一组(数十个为一组)http请求批量调用完成后再把请求响应结果异步存入数据库,
主线程使用了countDownLatch.await进行等待,
其中whenCompleteAsync方法存在空指针问题,导致没能执行如下的countDown方法。

这里有人会问, 上面错误日志检查步骤,不是说日志没有空指针异常吗?
对,子线程报了空指针,因为CompletableFuture执行每次都是new 一个新的CompletableFuture对象,并把结果作为下一个CompletableFuture执行的入参,
通过伪代码可以发现,执行whenCompleteAsync后,没有新的CompletableFuture方法执行,所以异常没有抛出来,使得排查变得困难

3.2 解决

因为存在whenCompleteAsync报错的情况,添加多一个新的异常捕获处理方法,捕获异常也进行countDown的操作。

代码如下:

    httpCall(tokenData).whenCompleteAsync((returnValue, ex)->{// do business// ex.getMessage()// 其中ex对象为空,使用ex.getMessage()报了空指针,导致没能执行如下的countDowncountDownLatch.countDown();}).exceptionally(e ->{log.info("exceptionally捕获到异常,tokenData={}, e={}", tokenData, e.getMessage());countDownLatch.countDown();return null;});

4 结论

  • 熟练CompletableFuture的使用,要看源码的实现(实现原理cas + 多个future采用入stack,每次把前一个future的结果作为参数传入下一个future去执行)

  • 使用多线程需要考虑异常、超时等情况

  • 熟练使用jvm stack分析工具

5 文章参考

CompletableFuture流程图

CompletableFuture参考文章如下

CompletableFuture 原理浅析

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

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

相关文章

企业如何高效平滑迁移数据?火山引擎DataLeap上线整库搬迁解决方案

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 近日&#xff0c;火山引擎大数据研发治理套件DataLeap上线整库搬迁解决方案&#xff0c;包括整库离线同步、整库实时同步两大能力&#xff0c;大大提升数据上云便捷…

C++项目实战——基于多设计模式下的同步异步日志系统-⑤-实用工具类设计

文章目录 专栏导读获取系统时间time介绍 getTime函数设计判断文件是否存在stat介绍exists函数设计 获取文件所在路径find_last_of介绍path函数设计 创建文件所在目录mkdir介绍find_first_of介绍函数createDirectory设计 实用工具类整理 专栏导读 &#x1f338;作者简介&#xf…

图论第三天|130. 被围绕的区域、417. 太平洋大西洋水流问题、827. 最大人工岛

130. 被围绕的区域 文档讲解 &#xff1a;代码随想录 - 130. 被围绕的区域 状态&#xff1a;开始学习。 思路&#xff1a; 步骤一&#xff1a; 深搜或者广搜将地图周边的 ‘O’ 全部改成 ’A’&#xff0c;如图所示&#xff1a; 步骤二&#xff1a; 再遍历地图&#xff0c;将 …

(3)MyBatis-Plus待开发

常用注解 TableName MyBatis-Plus在确定操作的表时&#xff0c;由BaseMapper的泛型决定即实体类型决定&#xff0c;且默认操作的表名和实体类型的类名一致,如果不一致则会因找不到表报异常 //向表中插入一条数据 Test public void testInsert(){User user new User(null, &…

基因型数据VCF转EXCEL亲测好用

import pandas as pd df pd.read_csv(shuju.vcf, sep\t, comment#, headerNone,encodingutf-8) df.to_excel(outputFile2.xlsx, indexFalse,encodingutf-8) 以上就是转成功的截图&#xff0c;需要注意的是一定要写入编码方式&#xff0c;UTF-8 &#xff0c;第一次我没有写编码…

Zabbix“专家坐诊”第203期问答汇总

问题一 Q&#xff1a;用的模板&#xff0c;没用创建动作&#xff0c;但是触发器触发了我钉钉直接被通知了&#xff0c;怎么取消模板自带的动作&#xff1f; A&#xff1a;没有动作是不会触发事件告警的 &#xff0c;确定是从这个平台推出的钉钉消息&#xff1f;或者看下zabbix…

论文解读 | 用于3D对象检测的PV-RCNN网络原创

原创 | 文 BFT机器人 01 背景 本文的背景涉及到3D物体检测&#xff0c;这是一个在自动驾驶和机器人等领域应用广泛的重要问题。在这些领域&#xff0c;LiDAR传感器被广泛用于捕捉3D场景信息&#xff0c;生成不规则且稀疏的点云数据。这些点云数据提供了理解和感知3D场景的关键…

数据可视化大屏模板 | 保姆级使用教程

近来很多朋友私信咨询怎么下载使用数据可视化大屏模板&#xff0c;在这里就给大家做一个相对简单的教程总结。有需要的朋友记得先收藏保存&#xff0c;以便不时之需。 数据可视化大屏制作软件&#xff1a;奥威BI系统 数据可视化报表模板板块&#xff1a;模板秀 主要操作&…

Linux 修改SSH的显示样式,修改终端shell显示的样式,美观更改

要修改SSH的显示样式&#xff0c;您可以使用自定义的PS1&#xff08;提示字符串1&#xff09;变量来更改命令行提示符的外观。在您的情况下&#xff0c;您想要的格式似乎包括日期和时间&#xff0c;以及当前目录。以下是一个示例PS1设置&#xff0c;可以实现您所描述的样式&…

【Seata】04 - Seata TCC 模式 Demo 调用流程分析

文章目录 前言参考目录版本说明前置知识1、TCC 模式预留资源2、TCC 模式可能会出现的问题2.1、幂等性问题2.2、空回滚问题2.3、悬挂问题 测试 Demo1、数据库表结构2、模块说明3、调用逻辑说明4、分析流程说明 Seata TCC 模式 Commit 调用流程1、调用流程图2、TCC 动作拦截器&am…

格式化以后数据还在吗 格式化后数据怎么恢复

格式化是一种保护数据存储设备、清空部分不使用文件的操作方法&#xff0c;它能够对数据存储设备中的文件进行初始化。 但是如果格式化时不小心清空了重要的数据&#xff0c;可能会造成数据丢失的问题。今天&#xff0c;我将以格式化以后数据还在吗&#xff0c;格式化后数据怎么…

第三方电容笔怎么样?开学季平价电容笔排行榜

苹果的电容笔能得到那么多人的认可&#xff0c;主要还是得益于这款电容笔的出色性能&#xff0c;以及它特有的“重力压感”这个特有功能。不过苹果的原装电容笔&#xff0c;价格也不便宜&#xff0c;大部分人还是用了平价的平替电容笔。现在很多人都喜欢用ipad来画画或者做记录…