前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MQ消费端线程“突然挂掉”?或许只是异常没catch

MQ消费端线程“突然挂掉”?或许只是异常没catch

作者头像
MickyInvQ
发布2022-09-26 21:48:29
5150
发布2022-09-26 21:48:29
举报
文章被收录于专栏:InvQ的专栏InvQ的专栏

文章目录

现场还原

消费端实现了MessageListenerConcurrently监听接口,然后实现了consumeMessage这个方法。 此方法中,我开了线程池去执行消费消息的逻辑,但是走到一行打印日志的代码时候,突然不执行了。

在这里插入图片描述
在这里插入图片描述

然后就没了,也没有报任何异常,下面的其他逻辑也没有执行。我怀疑是线程挂了。

排查–追踪线程

首先我排查下面的逻辑是否有问题, 发现没问题后,多打印了几个我觉得一定会打印的日志。结果发现,还是没有打印我觉得一定会打的日志。

其次,我开始追踪这个线程。 通过jps快速找到pid,jstack -l >temp.txt 命令快速将堆栈信息导出来。 观察这个mq-incr-pool-4线程在干嘛,是否存在等。 结果发现并没有这个mq-incr-pool-4线程,说明这个线程挂了。

在这里插入图片描述
在这里插入图片描述

那为啥会挂呢?还没有任何报错日志。

我尝试换成了其他打印的日志。再次观察。发现可以打出来,就我那条打不出来。

在这里插入图片描述
在这里插入图片描述

继续查看堆栈,线程仍然存活,因为个数没有超过核心数,会阻塞等待队列中的任务。

在这里插入图片描述
在这里插入图片描述

那么打印的对象是我通过@autowired注解进来的一个变量,然后是注入进来的时候没注入成功? 按理说spring启动容器的时候如果依赖有问题,应用会直接起不来。

于是我尝试性的,将@autowire注入改为了 构造注入。重新启动任务,发现ok了!~ 能打印出来这个注入的变量了! 这我就开始猜测,是否之前这个变量有问题,或许报了null指针,但是没有报异常。于是,我又手工构造了其他异常,放在这个方法里

代码语言:javascript
复制
  		Object Id = 10034432;
        long a = (long) Id;
        System.out.println(a);

这个正常是会报cast exception的

在这里插入图片描述
在这里插入图片描述

但是,如我所料,在这个方法里面,并没有打印任何异常。然后查看堆栈,发现线程也会像之前一样消失。

那就说明了, 这个方法里面的所以异常,如果你不自己try catch的话,那么就不会报,也不会打印。看源码便知道,

在这里插入图片描述
在这里插入图片描述

consumeMessage 方法中所有的异常,都会被catch住,日志会打到mq中间件日志里面,所以我这里并没有。

正确的操作应该是业务自行catch,类似下面这样

代码语言:javascript
复制
@Slf4j
@Component
public class Consumer {
    /**
     * 消费者实体对象
     */
    private DefaultMQPushConsumer consumer;
    /**
     * 消费者组
     */
    public static final String CONSUMER_GROUP = "test_consumer";
    /**
     * 通过构造函数 实例化对象
     */
    public Consumer() throws MQClientException {
        consumer = new DefaultMQPushConsumer(CONSUMER_GROUP);
        consumer.setNamesrvAddr("47.99.203.55:9876;47.99.203.55:9877");
        //订阅topic和 tags( * 代表所有标签)下信息
        consumer.subscribe("topic_family", "*");
        //注册消费的监听 并在此监听中消费信息,并返回消费的状态信息
        consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
            //1、获取消息
            Message msg = msgs.get(0);
            try {
                //2、消费者获取消息
                String body = new String(msg.getBody(), "utf-8");
                //3、获取重试次数
                int count = ((MessageExt) msg).getReconsumeTimes();
                log.info("当前消费重试次数为 = {}", count);
                //4、这里设置重试大于3次 那么通过保存数据库 人工来兜底
                if (count >= 2) {
                    log.info("该消息已经重试3次,保存数据库。topic={},keys={},msg={}", msg.getTopic(), msg.getKeys(), body);
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
                //直接抛出异常
                throw new Exception("=======这里出错了============");
                //return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            } catch (Exception e) {
                e.printStackTrace();
                return ConsumeConcurrentlyStatus.RECONSUME_LATER;
            }
        });
        //启动监听
        consumer.start();
    }
}

但是,为什么之前注入的有问题呢?改成构造注入就可以了呢?感兴趣的可以点我看下。

而我依赖注入的实例中,在它的构造器里面有一个稍微耗时的逻辑。

代码语言:javascript
复制
    public Client() {
        init();
    }

因为Field 注入允许构建对象实例的时候依赖的示例对象为空,这就导致了空指针异常无法尽早的暴露出来。而构造器是强依赖注入,就解决了这个问题。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 现场还原
  • 排查–追踪线程
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档