MQ消费端线程“突然挂掉”?或许只是异常没catch_MickyInvQ - 码子裤

技术标签:   网络安全  编程算法

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

作者:MickyInvQ


文章目录

现场还原

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

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

排查–追踪线程

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

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

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

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

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

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

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

  		Object Id = 10034432;
        long a = (long) Id;
        System.out.println(a);

这个正常是会报cast exception的

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

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

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

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

@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();
    }
}

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

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

    public Client() {
        init();
    }

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

本文参与 码字裤自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.csdn.net/uniquewonderq复制
如有侵权,请联系 heekey.com 删除。



 

 
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:http://www.heekey.com/article/weixin_2123860.html

智能推荐

快为你的Jupyter添加这7个扩展,效率upup! ⛵

兵欲善其事,必先利其器。对于数据科学家和数据开发工程师们来说,工具也是非常重要的,拥有好的工具会大大加速开发效率,并更快更准完成任务。

Spring AOP

Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由AopProxyFactory 根据 AdvisedSup...

Spring的自动装配

在spring中,使用autowire来配置自动装载模式,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象。

Spring如何解决循环依赖问题

(1)通过构造方法进行依赖注入时产生的循环依赖问题。 (2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。 (3)通过sett...

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

   ① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,在获...

Linux命令及文件操作

1.在/tmp/目录下创建test.txt文件,内容为:Hello,World!,用一个命令写出来。     答:     echo "Hello,Wor...