Dubbo3源码篇8-线程模型和线程池模型

638 阅读15分钟

欢迎大家关注 github.com/hsfxuebao ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈

1. 线程模型概述

Dubbo 默认的底层网络通信使用的是 Netty ,服务提供方 NettyServer 使用两级线程池,其中 EventLoopGroup(boss) 主要用来接收客户端的链接请求,并把完成 TCP 三次握手的连接分发给 EventLoopGroup(worker)来处理,我们把 boss 和 worker 线程组称为 I/O 线程 。

如果服务提供方的逻辑处理能迅速完成,并且不会发起新的 I/O 请求,那么直接在 I/O线程上处理会更快,因为这样减少了线程池调度与上下文切换的开销。

但如果处理逻辑较慢,或者需要发起新的 I/O 请求,比如需要查询数据库, 则 I/O 线程必须派发请求到新的线程池进行处理,否则 I/O 线程会被阻塞,导致不能接收其他请求。根据请求的消息类是被 I/O 线程处理还是被业务线程池处理, Dubbo 提供了下面几种线程模型。

  • all ( AllDispatcher 类〉:所有消息都派发到业务线程池,这些消息包括请求、响应、连接事件、断开事件、心跳事件等,如图所示:

    image.png

  • direct (DirectDispatcher 类):所有消息都不派发到业务线程池,全部在 IO 线程上直接执行。

  • message (MessageOnlyDispatcher 类):只有请求响应消息派发到业务线程池,其他消息如连接事件、断开事件、心跳事件等,直接在 I/O 线程上执行,如图所示: image.png

  • execution CExecutionDispatcher 类 ):只把请求类消息派发到业务线程池处理,但是 响应、连接事件、断开事件、心跳事件等消息直接在 I/O 线程上执行,如图所示:

image.png

  • connection ( ConnectionOrderedDispatcher 类 〉:在 I/O 线程上将连接事件、断开事件 放入队列,有序地逐个执行,其他消息派发到业务线程池处理,如图所示:

image.png

在 Dubbo 中,线程模型的扩展接口为 Dispatcher,其提供的上述扩展实现都实现了该接口,其中 all 模型是默认的线程模型。

2. 线程模型源码

2.1 AllDispatcher

public class AllDispatcher implements Dispatcher {

    // 线程模型的名称
    public static final String NAME = "all";

    // 扩展接口具体实现
    @Override
    public ChannelHandler dispatch(ChannelHandler handler, URL url) {
        return new AllChannelHandler(handler, url);
    }

}
其中, 核心 实现 Al lChannelf{andler 对应的代码如下:
public class AllChannelHandler extends WrappedChannelHandler {

    public AllChannelHandler(ChannelHandler handler, URL url) {
        super(handler, url);
    }

    // 连接完成事件,交给业务线程池处理
    @Override
    public void connected(Channel channel) throws RemotingException {
        ExecutorService executor = getExecutorService();
        try {
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CONNECTED));
        } catch (Throwable t) {
            throw new ExecutionException("connect event", channel, getClass() + " error when process connected event .", t);
        }
    }

    // 连接断开事件,交给业务线程池处理
    @Override
    public void disconnected(Channel channel) throws RemotingException {
        ExecutorService executor = getExecutorService();
        try {
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.DISCONNECTED));
        } catch (Throwable t) {
            throw new ExecutionException("disconnect event", channel, getClass() + " error when process disconnected event .", t);
        }
    }

    // 请求响应事件,交给业务线程池处理,当为请求分发器Dispatcher
    @Override
    public void received(Channel channel, Object message) throws RemotingException {
        // 线程池
        ExecutorService executor = getPreferredExecutorService(message);
        try {
            // 将对端请求/响应封装为一个任务
            // 从线程池中拿到一个线程,来处理这个任务。
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
        } catch (Throwable t) {
            if(message instanceof Request && t instanceof RejectedExecutionException){
                sendFeedback(channel, (Request) message, t);
                return;
            }
            throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
        }
    }

    // 异常处理事件,交给业务线程池处理
    @Override
    public void caught(Channel channel, Throwable exception) throws RemotingException {
        ExecutorService executor = getExecutorService();
        try {
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CAUGHT, exception));
        } catch (Throwable t) {
            throw new ExecutionException("caught event", channel, getClass() + " error when process caught event .", t);
        }
    }
}

通过上面的代码可知,这种线程模型把所有事件都直接交给业务线程池进行处理了。

2.2 DirectDispatcher

public class DirectDispatcher implements Dispatcher {

    public static final String NAME = "direct";

    @Override
    public ChannelHandler dispatch(ChannelHandler handler, URL url) {
        return new DirectChannelHandler(handler, url);
    }

}
核心实现对应的代码如下:
public class DirectChannelHandler extends WrappedChannelHandler {

    public DirectChannelHandler(ChannelHandler handler, URL url) {
        super(handler, url);
    }

    @Override
    public void received(Channel channel, Object message) throws RemotingException {
        ExecutorService executor = getPreferredExecutorService(message);
        if (executor instanceof ThreadlessExecutor) {
            try {
                executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
            } catch (Throwable t) {
                throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
            }
        } else {
            handler.received(channel, message);
        }
    }

}

通过上面的代码可知, 除了executor instanceof ThreadlessExecutor会走到业务线程池,direct 线程模型的 dispatch()方法直接返回了参数 handler , 所以其所有事件的处理都是在 I/O 线程上进行的。

2.3 MessageOnlyDispatcher

public class MessageOnlyDispatcher implements Dispatcher {

    public static final String NAME = "message";

    @Override
    public ChannelHandler dispatch(ChannelHandler handler, URL url) {
        return new MessageOnlyChannelHandler(handler, url);
    }
}
核心实现代码如下:
public class MessageOnlyChannelHandler extends WrappedChannelHandler {

    public MessageOnlyChannelHandler(ChannelHandler handler, URL url) {
        super(handler, url);
    }

    @Override
    public void received(Channel channel, Object message) throws RemotingException {
        ExecutorService executor = getPreferredExecutorService(message);
        try {
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
        } catch (Throwable t) {
            if(message instanceof Request && t instanceof RejectedExecutionException){
                sendFeedback(channel, (Request) message, t);
                return;
            }
            throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
        }
    }
}

通过上面的代码可知,只有请求响应消息派发到业务线程池 , 其他耗时比较短的连接事件、断开事件、心跳事件等消 息则直接在 1/0 线程上执行。

2.4 ExecutionDispatcher

public class ExecutionDispatcher implements Dispatcher {

    public static final String NAME = "execution";

    @Override
    public ChannelHandler dispatch(ChannelHandler handler, URL url) {
        return new ExecutionChannelHandler(handler, url);
    }

}
核心实现代码如下:
public class ExecutionChannelHandler extends WrappedChannelHandler {

    public ExecutionChannelHandler(ChannelHandler handler, URL url) {
        super(handler, url);
    }

    @Override
    public void received(Channel channel, Object message) throws RemotingException {
        ExecutorService executor = getPreferredExecutorService(message);

        // 只有把请求类消息派发到业务线程池处理
        if (message instanceof Request) {
            try {
                executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
            } catch (Throwable t) {
                // FIXME: when the thread pool is full, SERVER_THREADPOOL_EXHAUSTED_ERROR cannot return properly,
                // therefore the consumer side has to wait until gets timeout. This is a temporary solution to prevent
                // this scenario from happening, but a better solution should be considered later.
                if (t instanceof RejectedExecutionException) {
                    sendFeedback(channel, (Request) message, t);
                }
                throw new ExecutionException(message, channel, getClass() + " error when process received event.", t);
            }
        } else if (executor instanceof ThreadlessExecutor) {
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
        } else {
            // 但是响应和其他连接事件,断开事件,心跳事件等消息直接在I/O线程上执行
            handler.received(channel, message);
        }
    }
}

通过上面的代码可知,只把请求类消息派发到业务线程池处理,但是响应、连接事件 、断开事件、 心跳事件等消息则直接在 I/O 线程上执行。这里与 message 模型不同之处在于 ,响应类型的事件也是在 I/O 线程上执行的 。

2.5 ConnectionOrderedDispatcher

public class ConnectionOrderedDispatcher implements Dispatcher {

    public static final String NAME = "connection";

    @Override
    public ChannelHandler dispatch(ChannelHandler handler, URL url) {
        return new ConnectionOrderedChannelHandler(handler, url);
    }

}
核心代码如下:
public class ConnectionOrderedChannelHandler extends WrappedChannelHandler {

    protected final ThreadPoolExecutor connectionExecutor;
    private final int queuewarninglimit;

    // 构造函数
    // 在构造函数内创建一个只含有一个线程的线程池和一个有限元素的队列,如果设置的参数 
    // connect.queue.capacity 大于 0,则设置为线程池队列容量 , 否则线程池队列容量为整数最大值,这
    // 个线程池用来实现把链接建立和链接断开事件进行顺序化处理。
    public ConnectionOrderedChannelHandler(ChannelHandler handler, URL url) {
        super(handler, url);
        String threadName = url.getParameter(THREAD_NAME_KEY, DEFAULT_THREAD_NAME);
        // 创建线程池
        connectionExecutor = new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(url.getPositiveParameter(CONNECT_QUEUE_CAPACITY, Integer.MAX_VALUE)),
                new NamedThreadFactory(threadName, true),
                new AbortPolicyWithReport(threadName, url)
        );  // FIXME There's no place to release connectionExecutor!
        // 线程池队列元素限制警告
        queuewarninglimit = url.getParameter(CONNECT_QUEUE_WARNING_SIZE, DEFAULT_CONNECT_QUEUE_WARNING_SIZE);
    }

    // 连接建立事件
    @Override
    public void connected(Channel channel) throws RemotingException {
        try {
            checkQueueLength();
            connectionExecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CONNECTED));
        } catch (Throwable t) {
            throw new ExecutionException("connect event", channel, getClass() + " error when process connected event .", t);
        }
    }

    // 连接断开事件
    @Override
    public void disconnected(Channel channel) throws RemotingException {
        try {
            checkQueueLength();
            connectionExecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.DISCONNECTED));
        } catch (Throwable t) {
            throw new ExecutionException("disconnected event", channel, getClass() + " error when process disconnected event .", t);
        }
    }

    // 请求响应事件
    @Override
    public void received(Channel channel, Object message) throws RemotingException {
        ExecutorService executor = getPreferredExecutorService(message);
        try {
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
        } catch (Throwable t) {
            if (message instanceof Request && t instanceof RejectedExecutionException) {
                sendFeedback(channel, (Request) message, t);
                return;
            }
            throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
        }
    }

    // 异常事件
    @Override
    public void caught(Channel channel, Throwable exception) throws RemotingException {
        ExecutorService executor = getExecutorService();
        try {
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CAUGHT, exception));
        } catch (Throwable t) {
            throw new ExecutionException("caught event", channel, getClass() + " error when process caught event .", t);
        }
    }

    // 检查线程池队列元素个数
    private void checkQueueLength() {
        if (connectionExecutor.getQueue().size() > queuewarninglimit) {
            logger.warn(new IllegalThreadStateException("connectionordered channel handler `queue size: " + connectionExecutor.getQueue().size() + " exceed the warning limit number :" + queuewarninglimit));
        }
    }
}

对于链接建立、链接断开事件,可以先检查线程池队列的元素个数,个数超过阙值则打印日志,然后把事件放入线程池队列,并使用单线程进行处理。由于是单线程处理, 所以其实是“多生产 晴 单消费”模型,实现了把链接建立 、链接断开事件的处理变为顺序化处理。

处理请求事件和异常事件,这里是直接交给了线程池(这个线程池不是 connectionExecutor )进行异步处理。

3. 线程模型的确定时机

前面介绍了 Dubbo 提供 的线程模型, 下面我们介绍的是何时确定使用哪种线程模型。服务提供方会启动 NettyServer 来监听消费方的链接 , 其构造函数如下:

public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
    super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
}

主要看ChannelHandlers.wrap(handler, url)

public static ChannelHandler wrap(ChannelHandler handler, URL url) {
    return ChannelHandlers.getInstance().wrapInternal(handler, url);
}
protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
    return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
            .getAdaptiveExtension().dispatch(handler, url)));
}

这里根据 URL 里的线程模型来选择具体的 Dispatcher 实现类。 在此, 我们再提一下Dubbo 提供的 Dispatcher 实现类 ,其默认的实现类是 all。

4. 线程池模型

@SPI("fixed")
public interface ThreadPool {
    /**
     * Thread pool
     * @param url URL contains thread parameter
     * @return thread pool
     */
    @Adaptive({Constants.THREADPOOL_KEY})
    Executor getExecutor(URL url);

}

我们可以看到ThreadPool接口是个扩展点,然后默认实现是fixed,然后里面有个getExecutor方法,被@Adaptive注解修饰。在dubbo中ThreadPool有4个实现类,分别是: image.png

上面讲解 Dubbo 线程模型时提到,为了尽量早地释放 Netty 的 I/O 线程,某些线程模型会把请求投递到线程池进行异步处理,那么这里所谓的线程池是什么样的线程池呢?其实这里的线程池 ThreadPool 也是一个扩展接口 SPI, Dubbo 提供了该扩展接口的一些实现,具体如下。

  • FixedThreadPool :创建一个具有固定个数线程的线程池。
  • LimitedThreadPool :创建一个线程池,这个线程池中的线程个数随着需要量动态增加,但是数量不超过配置的阈值。另外,空闲钱程不会被回收,会一直存在。
  • EagerThreadPool :创建一个线程池,在这个线程池中,当所有核心线程都处于忙碌状态时,将创建新的线程来执行新任务,而不是把任务放入线程池阻塞队列 。
  • CachedThreadPool : 创建一个自适应线程池,当线程空闲 1 分钟时,线程会被回收:当有新请求到来时,会创建新线程。

5. 线程池策略

5.1 FixedThreadPool

public class FixedThreadPool implements ThreadPool {

    @Override
    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);
        // 使用JUC的ThreadPoolExecutor创建线程池
        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配置也没有作用。

这里把 ThreadPoolExecutor 的核心线程个数和最大线程个数都设置为 threads,所以创建 的线程池是固定线程个数 的 线程池。另外,当队列元素为 0 时, 阻塞队列使用的是 SynchronousQueue ; 当 队列元素 小于 0 时 ,使用的是无界阻塞队列 LinkedBlockingQueue ;当队列元素大于 0 时,使用的是有界的 LinkedBlockingQueue 。

最后 ,线程池拒绝策略选择了 AbortPoicyWithReport , 意味着当线程池队列己满并且线程池中线程都忙碌时,新来的任务会被丢弃,并抛出 RejectedExecutionException 异常。

5.2 LimitedThreadPool

public class LimitedThreadPool implements ThreadPool {

    @Override
    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);
        // JUC 的ThreadPoolExecutor创建线程池 
        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直接是最大的,也就是线程可以增大,但是不会收缩,原因是防止大流量请求过来,还得现创建线程。

5.3 EagerThreadPool

public class EagerThreadPool implements ThreadPool {

    @Override
    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;
    }
}

当core线程都很忙的时候,创建新线程,而不是将任务放入阻塞队列。 EagerThreadPool与上面几个线程池不一样的地方就是使用了自定义的EagerThreadPoolExecutor与自定义的taskQueue。我们先来看下线程池参数。

  • core:核心线程数,默认0
  • maxThreads:最大线程数,默认是Integer.MAX_VALUE
  • queue: 当queue>0的时候创建大小queue的TaskQueue,queue<=0的时候,就是0大小TaskQueue。默认0
  • keepalive:空闲时间,默认60 * 1000ms,也就是一分钟。 我们来看下TaskQueue队列源码
/**
 * TaskQueue in the EagerThreadPoolExecutor
 * It offer a task if the executor's submittedTaskCount less than currentPoolThreadSize
 * or the currentPoolThreadSize more than executor's maximumPoolSize.
 * That can make the executor create new worker
 * when the task num is bigger than corePoolSize but less than maximumPoolSize.
 */
public class TaskQueue<R extends Runnable> extends LinkedBlockingQueue<Runnable> {

    private static final long serialVersionUID = -2635853580887179627L;

    private EagerThreadPoolExecutor executor;

    public TaskQueue(int capacity) {
        super(capacity);
    }

    public void setExecutor(EagerThreadPoolExecutor exec) {
        executor = exec;
    }

    @Override
    public boolean offer(Runnable runnable) {
        if (executor == null) {
            throw new RejectedExecutionException("The task queue does not have executor!");
        }

        int currentPoolThreadSize = executor.getPoolSize();
        // have free worker. put task into queue to let the worker deal with task.
        if (executor.getSubmittedTaskCount() < currentPoolThreadSize) {
            return super.offer(runnable);
        }

        // return false to let executor create new worker.
        if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
            return false;
        }

        // currentPoolThreadSize >= max
        return super.offer(runnable);
    }

    /**
     * retry offer task
     *
     * @param o task
     * @return offer success or not
     * @throws RejectedExecutionException if executor is terminated.
     */
    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 继承LinkedBlockingQueue队列,然后重写了offer方法。我们看下这个方法,首先判断EagerThreadPoolExecutor对象是否存在,然后判断线程池当前的任务小于线程池大小,就说明空闲等待的线程,这时候,将任务放入队列,然后让线程去处理。如果当前的任务有很多,这时候判断当前线程数小于最大线程数的时候让线程池去创建线程,这些条件都不满足的时候才往队列里扔。

我们可以缕缕这个流程,当core有闲着的线程的时候,扔队列中让空闲线程处理,没有空闲线程的时候先创建线程,直到线程数到达最大线程数,这时候才会往队列里面扔。我们在源码中看到getSubmittedTaskCount这个方法,这个方法其实dubbo自定义实现ThreadPoolExecutor来维护的一个计数器。我们可以看下EagerThreadPoolExecutor的源码。 public class EagerThreadPoolExecutor extends ThreadPoolExecutor {

    /**
     * task count
     */
    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);
    }

    /**
     * @return current tasks which are executed
     */
    public int getSubmittedTaskCount() {
        return submittedTaskCount.get();
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        submittedTaskCount.decrementAndGet();
    }

    @Override
    public void execute(Runnable command) {
        if (command == null) {
            throw new NullPointerException();
        }
        // do not increment in method beforeExecute!
        submittedTaskCount.incrementAndGet();
        try {
            super.execute(command);
        } catch (RejectedExecutionException rx) {
            // retry to offer the task into queue.
            final TaskQueue queue = (TaskQueue) super.getQueue();
            try {
                if (!queue.retryOffer(command, 0, TimeUnit.MILLISECONDS)) {
                    submittedTaskCount.decrementAndGet();
                    throw new RejectedExecutionException("Queue capacity is full.");
                }
            } catch (InterruptedException x) {
                submittedTaskCount.decrementAndGet();
                throw new RejectedExecutionException(x);
            }
        } catch (Throwable t) {
            // decrease any way
            submittedTaskCount.decrementAndGet();
        }
    }
}

我们可以看到这个类里面维护了一个AtomicInteger类型的计数器。并重写了execute方法跟afterExecute方法。当来一个任务的时候,计数器先自增长1,然后任务交给父类处理,处理完会调用afterExecute方法,计数器自减1。父类拒绝的时候,重新往队列里offer,没成功的话计数器自减1,并抛出拒绝策略。中断异常的时候也是减1,抛出拒绝策略。父类抛出其他异常的时候也都是减1。

EagerThreadPoolExecutor 与 JUC 包中 的 ThreadPoolExecutor 不同之处在于,对于后者 来说, 当线程池核心线程个数达到设置的阈值时, 新来的任务会被放入线程池队列, 等队列满了 以后,才会开启新线程来处理任务( 前提是当前线程个数没有超过线程池最大线程个数〉 ;而对于前者来说,当线程池核心线程个数达到设置 的阈值时 , 新来的任务不会被放入线程池队列 , 而是会开启新线程来处理任务(前提是当前线程个数没有超过线程池最大线程个数〉 , 当线程个数达到最大线程个数时 ,才会把任务放入线程池队列 。

5.4 CachedThreadPool

public class CachedThreadPool implements ThreadPool {

    @Override
    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使用默认参数的话,就会无限创建线程,然后超过空闲时间,线程就会被销毁,然后再使用的时候,就会再创建。

6. 线程池的确定时机

到这里介绍完了线程模型的加载位置,但线程模型中的线程池 SPI 扩展什么时候加载 呢?这里以线程模型 AllDispatcher 为例做介绍。线程模型 AllDispatcher 对应的处理器是 AllChannelHandler, 我们直接看一下 链接完成事件对应的方法:

public class AllChannelHandler extends WrappedChannelHandler {

    // 连接完成事件,交给业务线程池处理
    @Override
    public void connected(Channel channel) throws RemotingException {
        ExecutorService executor = getExecutorService();
        try {
            executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.CONNECTED));
        } catch (Throwable t) {
            throw new ExecutionException("connect event", channel, getClass() + " error when process connected event .", t);
        }
    }
}

org.apache.dubbo.remoting.transport.dispatcher.WrappedChannelHandler#getExecutorService:

public ExecutorService getExecutorService() {
    return getSharedExecutorService();
}
public ExecutorService getSharedExecutorService() {
    ExecutorRepository executorRepository =
            ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
    ExecutorService executor = executorRepository.getExecutor(url);
    if (executor == null) {
        executor = executorRepository.createExecutorIfAbsent(url);
    }
    return executor;
}

接下来org.apache.dubbo.common.threadpool.manager.DefaultExecutorRepository#createExecutorIfAbsent:

public ExecutorService getSharedExecutorService() {
    ExecutorRepository executorRepository =
            ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
    ExecutorService executor = executorRepository.getExecutor(url);
    if (executor == null) {
        executor = executorRepository.createExecutorIfAbsent(url);
    }
    return executor;
}

private ExecutorService createExecutor(URL url) {
    return (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
}

其默认值为fixed

参考文章

Dubbo3.0源码注释github地址
深度剖析Apache Dubbo核心技术内幕
dubbo源码系列
dubbo源码分析专栏