概述
我们上篇文章讲到,Dubbo提供了多种线程模型,是为了尽早的释放I/O线程,把耗时的业务分派给业务线程池,那么这里的线程池是什么类型的线程池呢?是我们所熟知的JAVA线程池吗?并不是,这里的线程池,也是Dubbo的扩展接口ThreadPool。
ThreadPool
Dubbo为我们提供了四种类型的线程池,分别是
1、FixedThreadPool(默认),固定线程数的线程池。
2、LimitedThreadPool,线程池的线程数随着并发量动态增加,但不会超过配置的阈值。
3、EagerThreadPool,当提交的任务超过线程池的配置而触发拒绝策略后,会再次将任务投入队列。
4、CachedThreadPool,如果线程池里的线程空闲一定时间后,会回收线程。
源码分析
要了解Dubbo为我们提供了哪些线程池,我们首先要看一看ThreadPool这个扩展点接口的代码
@SPI("fixed")
public interface ThreadPool {
@Adaptive({Constants.THREADPOOL_KEY})
Executor getExecutor(URL url);
}
可以看到,该接口上的SPI注解,表明该方法是一个扩展点接口。该接口只有一个方法,getExecutor,用来获取线程池,返回Executor的实例,而这个Executor是JAVA并发包下的java.util.concurrent.Executor。
下面我们一一分析每种类型线程池的源码
FixedThreadPool类,固定数量的线程池:
public class FixedThreadPool implements ThreadPool {
@Override
public Executor getExecutor(URL url) {
// 获取线程池名字
String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
// 获取线程数量
int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
// 获取等待队列大小
int queues = url.getParameter(Constants.QUEUES_KEY, Constants.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));
}
}
源码很简单,通过URL参数获取创建线程池的各种参数,包括线程名字,线程数量和等待队列大小,然后通过JAVA并发包下的java.util.concurrent.ThreadPoolExecutor创建线程池。注意,线程池的corePoolSize和maximumPoolSize都是一样的哦。
LimitedThreadPool类:
public class LimitedThreadPool implements ThreadPool {
@Override
public Executor getExecutor(URL url) {
// 获取线程名
String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
// 获取corePoolSize
int cores = url.getParameter(Constants.CORE_THREADS_KEY, Constants.DEFAULT_CORE_THREADS);
// 获取maximumPoolSize
int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
// 获取等待队列大小
int queues = url.getParameter(Constants.QUEUES_KEY, Constants.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));
}
}
与FixedThreadPool很类似,但是不同的是,创建线程池时,corePoolSize和maximumPoolSize参数不同,这里就不多解释JAVA线程池的原理了。另外,空闲的线程不会回收,会一直存在,因为线程池的keepAliveTime是Long.MAX_VALUE,这一点和FixedThreadPool不同。
EagerThreadPool类:
public class EagerThreadPool implements ThreadPool {
@Override
public Executor getExecutor(URL url) {
String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
int cores = url.getParameter(Constants.CORE_THREADS_KEY, Constants.DEFAULT_CORE_THREADS);
int threads = url.getParameter(Constants.THREADS_KEY, Integer.MAX_VALUE);
int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
int alive = url.getParameter(Constants.ALIVE_KEY, Constants.DEFAULT_ALIVE);
// 初始化taskQueue
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;
}
}
与其他代码类似,但是不同的是,这里的等待队列,不是JDK的LindedBlokingQueue,而是一个TaskQueue,返回的executor不是JDK中的Executor而是EagerThreadPoolExecutor!!!我们首先看一下TaskQueue的代码
public class TaskQueue<R extends Runnable> extends LinkedBlockingQueue<Runnable> {
private EagerThreadPoolExecutor executor;
public void setExecutor(EagerThreadPoolExecutor exec) {
executor = exec;
}
@Override
public boolean offer(Runnable runnable) {
if (executor == null) {
throw new RejectedExecutionException("...");
}
int currentPoolThreadSize = executor.getPoolSize();
if (executor.getSubmittedTaskCount() < currentPoolThreadSize) {
// 入队
return super.offer(runnable);
}
if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
return false;
}
return super.offer(runnable);
}
public boolean retryOffer(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {
if (executor.isShutdown()) {
throw new RejectedExecutionException("Executor is shutdown!");
}
return super.offer(o, timeout, unit);
}
}
删掉一些不重要的代码可发现,TaskQueue继承了LinkedBlokingQueue,覆写了offer方法。并且在构造方法里初始化了EagerThreadPoolExecutor,我们分析一下EagerThreadPoolExecutor的源码
public class EagerThreadPoolExecutor extends ThreadPoolExecutor {
private final AtomicInteger submittedTaskCount = new AtomicInteger(0);
public EagerThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit, TaskQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
public int getSubmittedTaskCount() {
return submittedTaskCount.get();
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
submittedTaskCount.decrementAndGet();
}
@Override
public void execute(Runnable command) {
// ...
submittedTaskCount.incrementAndGet();
try {
super.execute(command);
} catch (RejectedExecutionException rx) {
// 如果线程池满导致执行拒绝策略后
final TaskQueue queue = (TaskQueue) super.getQueue();
try {
// 将任务入队TaskQueue
if (!queue.retryOffer(command, 0, TimeUnit.MILLISECONDS)) {
throw new RejectedExecutionException("Queue capacity is full.", rx);
}
} catch (InterruptedException x) {
// ...
}
} catch (Throwable t) {
// ...
}
}
}
这段代码就很有意思,我们看核心代码execute方法,如果线程池满载执行拒绝策略后,会将任务再次投递到TaskQueue中。
CachedThreadPool类:
public class CachedThreadPool implements ThreadPool {
@Override
public Executor getExecutor(URL url) {
// 获取线程名
String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
// 获取corePoolSize
int cores = url.getParameter(Constants.CORE_THREADS_KEY, Constants.DEFAULT_CORE_THREADS);
// 获取maximumPoolSize
int threads = url.getParameter(Constants.THREADS_KEY, Integer.MAX_VALUE);
int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
// 获取线程的空闲时间
int alive = url.getParameter(Constants.ALIVE_KEY, Constants.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));
}
}
与FixedThteadPool和LimitedThreadPool类型线程池不同的是,这里设置了线程池的keepAliveTime参数,当线程空闲时间达到后,会回收线程。
线程池的确定时机
那么,Dubbo是在什么时候确定使用什么类型的线程池呢?我们以AllDispatcher线程模型为例(AllDispatcher线程模型参考:Apache Dubbo系列:Dubbo线程模型)
public class AllDispatcher implements Dispatcher {
public static final String NAME = "all";
@Override
public ChannelHandler dispatch(ChannelHandler handler, URL url) {
return new AllChannelHandler(handler, url);
}
}
public class AllChannelHandler extends WrappedChannelHandler {
public AllChannelHandler(ChannelHandler handler, URL url) {
super(handler, url);
}
// ...
}
AllDispatcher的disparch返回的AllChannelHandler的构造方法中,调用父类即WrappedChannelHandler的构造方法,我们继续跟进
public WrappedChannelHandler(ChannelHandler handler, URL url) {
this.handler = handler;
this.url = url;
// Dubbo SPI机制,加载ThreadPool
executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
// ...
}
这里,还是通过Dubbo SPI机制,加载ThreadPool这个扩展点。所以大家,Dubbo的SPI机制真的是贯穿于整个Dubbo框架,请大家务必熟悉Dubbo SPI(推荐阅读:Apache Dubbo系列:增强SPI)
如何扩展线程池
那么我们在Dubbo原有的线程池的基础上,我们怎么扩展自己的线程池呢?
很简单,我们要实现ThreadPool接口并覆写getExecutor方法,伪代码如下:
class MyThreadPool implements ThreadPool{
@Override
public Executor getExecutor(URL url) {
return new ThreadPoolExecutor(1,1,0, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
}
}
还要在/META-INF/dubbo目录下创建org.apache.dubbo.common.threadpool.ThreadPool文件,文件内容是mythreadpool=com.xxx.MyThreadPool,并修改相关配置文件即可。
好了,Dubbo的线程池策略,今天就为大家介绍到这里,如果有不正确的地方或有疑问,请留言指出,谢谢大家。
推荐阅读:
1万+

被折叠的 条评论
为什么被折叠?



