Dubbo之线程模型
以netty传输层实现为例
Dubbo的线程模型其实就是netty的线程模型,主从Refactor模型。
主Refactor用于建立连接,子Refactor用于监听所有事件进行处理。子Refactor在处理事件时,通常会把读事件交给业务线程池处理。
Dubbo对于这些事件的分发逻辑封装到Dispatcher扩展点
事件对应关系
为了保证Dubbo传输层不依赖于特定框架,Dubbo封装了自己的事件接口用于适配各个传输层框架。下面是dubbo和netty中事件接口的对应关系。
Netty | Dubbo | 描述 |
---|---|---|
channelActive | connected | 连接 |
channelInactive | disconnected | 断连 |
channelRead | received | 收到报文 |
write | sent | 发送报文 |
exceptionCaught | caught | 发生异常 |
userEventTriggered | 使用netty接口 | 用户事件 |
Dispatcher扩展点
而我们的Dispatcher扩展点会决定在上面的事件中使用那些线程池。
Dispatcher | connected | disconnected | received | sent | caught |
---|---|---|---|---|---|
AllDispatcher(默认) | 业务线程池 | 业务线程池 | 业务线程池 | IO线程池 | 业务线程池 |
ConnectionOrderedDispatcher | 连接专属线程池 | 连接专属线程池 | 业务线程池 | IO线程池 | 业务线程池 |
DirectDispatcher | IO线程池 | IO线程池 | IO线程池 | IO线程池 | IO线程池 |
ExecutionDispatcher | IO线程池 | IO线程池 | 业务线程池(只针对Request) | IO线程池 | IO线程池 |
MessageOnlyDispatcher | IO线程池 | IO线程池 | 业务线程池(全部) | IO线程池 | IO线程池 |
IO线程池是Netty 从Refactor所在的线程池,也就是EventLoop。
业务线程池指的是Dubbo的ThreadPool扩展点生成的线程池,创建逻辑如下。
executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
关于Dubbo的ThreadPool扩展点,可以看[这篇文章](https://www.jianshu.com/p/3decaaa00e00](https://www.jianshu.com/p/3decaaa00e00)。
连接专属线程池是在ConnectionOrderedDispatcher模式下专门创建用于处理连接的一个线程池,逻辑如下
connectionExecutor = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(url.getPositiveParameter(Constants.CONNECT_QUEUE_CAPACITY, Integer.MAX_VALUE)),
new NamedThreadFactory(threadName, true),
new AbortPolicyWithReport(threadName, url)
);
整体dispatcher的逻辑如下
最佳实践
Dubbo的默认使用AllDispatcher和core=max=200,queue为SynchronousQueue的FixedThreadPool。
这会出现一个问题,当FixedThreadPool中的200个线程都处于忙碌状态时,我们新的请求会被拒绝抛出异常,而这个异常会被caught方法捕获,但是caught方法也在这个业务线程池中执行,可能这个任务还是会被拒绝,客户端只能等到超时,抛出超时异常。
超时异常是相当的恐怖,我们无法确认它背后是否执行以及是否成功。
所以建议设置ThreadPool的queue为一个合理的数字,并且使用MessageOnlyDispatcher或ExecutionDispatcher。