之前线上代码Xstream代码的GC问题,导致dubbo服务访问的时候,出现线程不足,无法处理业务线程。因为出现了这个问题,我就看了看dubbo线程池。
1.介绍
当我们在使用dubbo的时候,是可以通过调整线程池来达到调优的效果,我们可以在dubbo:protocol 标签中使用用threadpool属性选择自己想要使用的线程池,通过threads属性配置服务线程数,queues属性配置使用的队列。例如:
<dubbo:protocol name="dubbo" threadpool="fixed" queues="100" threads="100"/>
dubbo的官网地址:http://dubbo.apache.org/
dubbo官网里面有源码介绍http://dubbo.apache.org/zh/docs/v2.7/dev/,还有博客地址http://dubbo.apache.org/zh/blog/news/,都是不错的内容。
dubbo的线程池在dubbo的org.apache.dubbo.common.threadpool包下面:
2.ThreadPool接口
/**
* ThreadPool
*/
@SPI("fixed")
public interface ThreadPool {
/**
* Thread pool
*
* @param url URL contains thread parameter
* @return thread pool
*/
@Adaptive({THREADPOOL_KEY})
Executor getExecutor(URL url);
}
我们可以看到ThreadPool接口是个扩展点,然后默认实现是fixed,然后里面有个getExecutor方法,被@Adaptive注解修饰。在dubbo中ThreadPool有4个实现类,分别是:
- CachedThreadPool 缓存线程池,超过keepAliveTime时间删除,使用的时候再创建
- FixedThreadPool 固定线程数量线程池,一旦建立,一直持有。
- LimitedThreadPool 可伸缩线程池,线程只增长不收缩。
- EagerThreadPool 当core线程数忙的时候,创建新线程,而不是将任务放入阻塞队列。这个使用自己队列TaskQueue。
3.CachedThreadPool
public Executor getExecutor(URL url) {
String name = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);
int cores = url.getParameter(CORE_THREADS_KEY, DEFAULT_CORE_THREADS);
int threads = url.getParameter(THREADS_KEY, Integer.MAX_VALUE);
int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);
int alive = url.getParameter(ALIVE_KEY, DEFAULT_ALIVE);
return new ThreadPoolExecutor(cores, threads, alive, TimeUnit.MILLISECONDS,
queues == 0 ? new SynchronousQueue<Runnable>() :
(queues < 0 ? new LinkedBlockingQueue<Runnable>()
: new LinkedBlockingQueue<Runnable>(queues)),
new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));
}
- core:核心线程数,默认是0;
- maxThread:最大线程数,默认是Integer.MAX_VALUE,可以看作是无限大。
- queues:如果queues=0,使用SynchronousQueue,如果是小于0,就是个new
LinkedBlockingQueue
队列,这个队列大小是Integer.MAX_VALUE,这个查看LinkedBlockingQueue源码可以看到。如果是queues大于0
,就创建queues大小的LinkedBlockingQueue,默认是0 。 - keepalive:最大空闲时间,默认是 60 * 1000ms ,也就是1分钟。
我们可以看到CachedThreadPool使用默认参数的话,就会无限创建线程,然后超过空闲时间,线程就会被销毁,然后再使用的时候,就会再创建。
4.FixedThreadPool
public Executor getExecutor(URL url) {
String name = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);
int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);
int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);
return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,
queues == 0 ? new SynchronousQueue<Runnable>() :
(queues < 0 ? new LinkedBlockingQueue<Runnable>()
: new LinkedBlockingQueue<Runnable>(queues)),
new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));
}
- core:核心线程数,默认是200
- maxThreads: 最大线程数。默认是200
- queues:当queues<0,使用一个无限大的LinkedBlockingQueue队列,当queues>=0
的时候创建queues大小的LinkedBlockingQueue。默认是0,也是创建0大小的LinkedBlockingQueue队列。 - keepalive:直接就是0,表示不销毁。 我们可以看出FixedThreadPool
创建固定大小的线程池,默认是200,期间不会销毁,使用了FixedThreadPool线程池,keepalive配置也没有作用。
5.LimitedThreadPool
public Executor getExecutor(URL url) {
String name = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);
int cores = url.getParameter(CORE_THREADS_KEY, DEFAULT_CORE_THREADS);
int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);
int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);
return new ThreadPoolExecutor(cores, threads, Long.MAX_VALUE, TimeUnit.MILLISECONDS,
queues == 0 ? new SynchronousQueue<Runnable>() :
(queues < 0 ? new LinkedBlockingQueue<Runnable>()
: new LinkedBlockingQueue<Runnable>(queues)),
new NamedInternalThreadFactory(name, true), new AbortPolicyWithReport(name, url));
}
- core:核心线程数,默认是0
- maxThreads: 最大线程数,默认是200
- queues:当queues=0的时候使用SynchronousQueue,当queues <
0的时候创建一个无限大小LinkedBlockingQueue队列,当queues>0的时候,创建queues大小的LinkedBlockingQueue队列。默认是0 - keepalive:直接就是Long.MAX_VALUE。
我们可以看到keepalive直接是最大的,也就是线程可以增大,但是不会收缩,原因是防止大流量请求过来,还得现创建线程。
6.EagerThreadPool
public Executor getExecutor(URL url) {
String name = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);
int cores = url.getParameter(CORE_THREADS_KEY, DEFAULT_CORE_THREADS);
int threads = url.getParameter(THREADS_KEY, Integer.MAX_VALUE);
int queues = url.getParameter(QUEUES_KEY, DEFAULT_QUEUES);
int alive = url.getParameter(ALIVE_KEY, DEFAULT_ALIVE);
// init queue and executor
TaskQueue<Runnable> taskQueue = new TaskQueue<Runnable>(queues <= 0 ? 1 : queues);
EagerThreadPoolExecutor executor = new EagerThreadPoolExecutor(cores,
threads,
alive,
TimeUnit.MILLISECONDS,
taskQueue,
new NamedInternalThreadFactory(name, true),
new AbortPolicyWithReport(name, url));
taskQueue.setExecutor(executor);
return executor;
}
EagerThreadPoolExecutor继承ThreadPoolExecutor,重写了ThreadPoolExecutor的afterExecute,execute方法。EagerThreadPoolExecutor中一个AtomicInteger变量submittedTaskCount用于记录线程池正在运行的任务数
Override
protected void afterExecute(Runnable r, Throwable t) {
// 提交任务执行结束,任务数减1
submittedTaskCount.decrementAndGet();
}
@Override
public void execute(Runnable command) {
if (command == null) {
throw new NullPointerException();
}
// 提交的任务增加1
submittedTaskCount.incrementAndGet();
try {
super.execute(command);
} catch (RejectedExecutionException rx) {
// 被线程池策略拒绝,重试机制
final TaskQueue queue = (TaskQueue) super.getQueue();
try {
if (!queue.retryOffer(command, 0, TimeUnit.MILLISECONDS)) {
// 队列重试失败,提交任务数减1
submittedTaskCount.decrementAndGet();
// 抛出Queue capacity is full
throw new RejectedExecutionException("Queue capacity is full.", rx);
}
} catch (InterruptedException x) {
// 线程异常,提交任务数减1
submittedTaskCount.decrementAndGet();
throw new RejectedExecutionException(x);
}
} catch (Throwable t) {
// decrease any way
submittedTaskCount.decrementAndGet();
throw t;
}
}
EagerThreadPool还自定义了自己的线程队列,TaskQueue extends LinkedBlockingQueue,重写了队列的
offer方法。
public boolean offer(Runnable runnable) {
if (executor == null) {
throw new RejectedExecutionException("The task queue does not have executor!");
}
// 线程池目前运行的线程
int currentPoolThreadSize = executor.getPoolSize();
// 提交的任务数小于当前运行的线程数 直接提交就能被消费
if (executor.getSubmittedTaskCount() < currentPoolThreadSize) {
return super.offer(runnable);
}
// 提交的任务大于当前线程数目,且当前线程数小于最大线程数,等待线程线程创建
if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
return false;
}
// 当起达到最大线程池提交任务
return super.offer(runnable);
}
参考文章:https://blog.csdn.net/yuanshangshenghuo/article/details/106454660