背景:
最近在工作中查了一个问题,消费者队列里面的消息无法没有成功消费。
问题描述:
这里生产者会将消息放入队列中,由消费者消费消息。startLoop是一个消费者。
在生产环境中发现依然有生产者往队列里面发送消息,但是就是没有消费。
private AtomicBoolean isLooping = new AtomicBoolean(false);
private BlockingQueue<T> queue = new LinkedBlockingQueue<>(50000);
public boolean add(T t) {
try {
queue.put(t);
if (!isLooping.get()) {
isLooping.set(true);
startLoop();
}
return true;
} catch (Exception e) {
logger.error(this, e);
}
return false;
}
private void startLoop() {
new Thread(() -> {
start = new AtomicLong(System.currentTimeMillis());
while (true) {
try {
long last = System.currentTimeMillis() - start.get();
if (processEnabled && (queue.size() >= batchSize || (!queue.isEmpty() && last > timeoutInMs))) {
drainToConsume();
} else if (queue.isEmpty()) {
isLooping.set(false);
break;
}
} catch (Throwable e) {
logger.error("Batch process with error.", e);
isLooping.set(false);
break;
}
}
}, this.name + BATCH_COMMIT_THREAD).start();
问题研究:
首先查看jstack日志:
可以发现这里发生了阻塞。BatchQueue里面是一个阻塞队列,通过debug查看到队列的大小达到最大了,说明islooping为ture。走查了startLoop的代码,只要走到这里面,islooping最后肯定会被设置成fasle。除非这个线程就没有启动起来(排查的时候觉得基本不可能啊,哪有线程没有启动的现象)
继续研究jstack日志:
发现有4000多个线程abc_xxx这种,通过走查代码,发现有一个地方在频繁创建线程。最后导致线程数量不够用,java.lang.OutOfMemoryError: unable to create new native thread出现了这个异常,但是我们的异常没有捕获成功。
至此,问题已经找到
问题修复:
1 业务侧一定不能出现频繁创建线程的代码
2 修复代码中的Exception。并且抛异常后修改为是否循环修改为false