专栏首页InvQ的专栏MQ消费端线程“突然挂掉”??或许只是异常没catch

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

文章目录

现场还原

消费端实现了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 注入允许构建对象实例的时候依赖的示例对象为空,这就导致了空指针异常无法尽早的暴露出来。而构造器是强依赖注入,就解决了这个问题。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一句话解释synchronized 锁优化技术以及解释两个线程同时访问synchronized如何保证线程安全

    主要有: 1.自旋锁——不放弃处理器时间,毕竟为了锁定状态那点时间挂起和回复线程不值得。该功能默认关闭,可自行开启,自旋默认10次,可以自行更改。

    MickyInvQ
  • Error instantiating interface xxxMapper with invalid types () or values ()

    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis...

    MickyInvQ
  • 线程的调度方式——抢占式、非抢占式

    在抢占模式下,操作系统负责分配CPU时间给各个进程,一旦当前的进程使用完分配给自己的CPU时间,操作系统将决定下一个占用CPU时间的是哪一个线程。因此操作系统...

    MickyInvQ
  • Java Thread wait、notify与notifyAll

    Tencent JCoder
  • 事务处理的数据存储

    在上篇文章我们讨论了数据模型,今天试着讨论更基础的数据存储和搜索。数据存储根据开发者使用,可以分为一般的事务处理和数据分析,因为这两者面临的情况不一样。事务处理...

    哒呵呵
  • ESP8266继电器控制之网页控制

    设置函数:将继电器的 pin 设置为输出,串行通信波特率为9600,然后调用 connectToWiFi。指定函数handleRoot,当有人向服务器发出web...

    云深无际
  • python基础 -- acm

    The input will consist of a series of pairs of integers a and b,separated by a s...

    lpe234
  • java多线程技术总结

    用户2196435
  • 微信小游戏之旅1. 起步

    2.之前带领团队做过小程序开发,自己也做过小程序开发(我的小程序:为了考PMP,我做了一个刷题小程序),但是没做过小游戏。

    悟空聊架构
  • js new Date() 默认是8点

    最近在写一个页面,需要用到时间控制。然后我通过new Date()传入日期字符串创建了一个对象,并与当前时间做时间戳比较,结果12点刚过,就出问题了。举个栗子

    烟草的香味

扫码关注云+社区

领取腾讯云代金券