背景
某个服务包括后台多个消息队列消费者线程,由线程池管理。由于是消费者线程,业务逻辑都形如while(true),理论上应该一直处于运行状态,但某个环境服务产出异常,查看服务进程正常、但无消费消息日志,日志保留时间有限。此时应该进入消息队列控制台,查看消息消费情况和消费者连接情况,但由于非技术原因无法马上检查,因此只能从服务自身状态去定位。
分析
通过jps和jstack查看了服务进程状态,如下图所示。
可以看到对应线程池的线程都处于WAITING状态,说明对应业务任务已经执行完成,等待新提交的任务。后续分析是while(true)中某个逻辑导致消费者线程“异常退出”,这里不再赘述。
复现示例代码
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
int backThreadCount = 3;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(backThreadCount, backThreadCount,
60, TimeUnit.SECONDS, new SynchronousQueue<>(),
new CustomizableThreadFactory("back-thread-pool-"));
for(int i=0;i<backThreadCount;i++) {
threadPoolExecutor.submit(() -> {
System.out.println(Thread.currentThread().getName() + " start");
if(true) {
throw new IllegalArgumentException("some exception");
}
while (true) {
// do something
}
});
}
while(true) {
// mock monitor the thread pool, not for prod
Thread.sleep(5000L);
System.out.println("threadPoolExecutor activeCount: " + threadPoolExecutor.getActiveCount());
}
}
}
控制台输出:
back-thread-pool-1 start
back-thread-pool-3 start
back-thread-pool-2 start
threadPoolExecutor activeCount: 0
threadPoolExecutor activeCount: 0
小结
- 线程池指定名称非常重要,服务大量使用线程池,需要根据名称定位线程状态;
- 对于关键的线程池,进行线程池监控非常有必要(如示例代码 28 行所示),能够快速定位问题!
- 对于消息队列消费者任务/线程,初始化等“不能出现异常”的逻辑应该放在
while(true)之外。
其实这个问题很简单,简单使用jps和jstack根据线程状态就能定位,这里仅是简单记录(大佬勿喷)。大家有遇到什么疑难杂症或有趣的问题欢迎在评论区留言贴链接,感谢。