如果某个Dubbo请求并发比较高,同时响应上由于数据库原因或者网络原因导致接口内部请求慢,则该Dubbo方法及其容易导致Dubbo里的线程池耗尽,此时消费端会收到如下异常堆栈信息
Caused by: java.util.concurrent.RejectedExecutionException: Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-192.168.112.12:8045, Pool Size: 200 (active: 200, core: 200, max: 200, largest: 200), Task: 165633 (completed: 165433), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://192.168.112.12:8045!
at com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport.rejectedExecution(AbortPolicyWithReport.java:53)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:768)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:656)
at com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.caught(AllChannelHandler.java:65)
报错核心信息是Thread pool is EXHAUSTED
,说明是线程池耗尽了,同时注意到如下信息, Pool Size: 200 (active: 200, core: 200, max: 200, largest: 200),说明Dubbo中默认最大200个线程,此时必然已经达到了最大线程数,再创建线程时导致系统异常,那么Dubbo中默认的线程池是哪种呢,为什么会抛出这个异常?
本文以此异常为出发点,分析Dubbo里为什么会出现这种异常以及线程池配置。
瞎想的面试题
1. JDK线程池核心参数有哪些?
2. Java常见线程池有哪些,分别有什么问题?
3. Dubbo中的有哪几种线程池?
4. Dubbo中的线程池和JDK中的有什么区别?
JDK中线程池参数
了解Dubbo中的线程池时,有必要先从最基本的JDK中的线程池出发,先看下ThreadPoolExecutor
的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
先分别解释下各个参数
参数 | 含义 |
---|---|
corePoolSize | 核心线程数,当线程池中线程数量小于corePoolSize时,会不断创建新线程,直到数量到达corePoolSize |
maximumPoolSize | 线程池中容许最大的线程数 |
keepAliveTime | 线程存活时间,如果当前线程数> corePoolSize,多余的线程会存活keepAliveTime时间,之后被回收释放 |
workQueue | 线程池任务缓存队列 |
threadFactory | 创建线程的工厂,通常用该该工厂来指定创建线程的名称 |
handler | 线程池中线程到达maximumPoolSize最大线程数后,再提交任务会使用handler拒绝策略来拒绝提交 |
举个实际例子来说明,假设现在创建如下线程池
private static Executor statisticThreadPool = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1), new ThreadFactoryBuilder().setNameFormat("test-thread-poot-%d").build(), (r, executor) -> {
log.info("丢弃统计任务处理");
});