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 注入允许构建对象实例的时候依赖的示例对象为空,这就导致了空指针异常无法尽早的暴露出来。而构造器是强依赖注入,就解决了这个问题。
相关文章
- 企业微信通讯录回调密文解析及微信支付回调密文解析异常处理
- Scrapy的Meta、异常处理
- Python抛出异常_python抛出异常的作用
- JAVA自定义业务异常类
- RxJava 异常时堆栈显示不正确?解决方法都在这里
- AI视频大数据智能分析平台EasyCVR因通道达到上限出现播放异常的排查
- 如何处理JDK线程池内线程执行异常?讲得这么通俗,别还搞不懂
- Oracle ORA-22908(NULL表值的参考)异常分析与解决方法
- Oracle数据库异常捕获与处理(oracle异常捕获)
- 解决 Linux 异常进程问题的方法(linux异常进程)
- 处理 Oracle 非预定义异常的方法(oracle非预定义异常)
- Oracle数据库写入异常卡顿问题(oracle写入卡顿)
- 解决Oracle主键自增异常的办法(oracle主键自增异常)
- Oracle数据库系统异常捕获机制研究(oracle中捕获异常)
- 抓住Redis异常跳出热门技术框架中的捕获(redis要捕获的异常)
- java多线程中的异常处理机制简析
- php异常处理使用示例