导读
我们都知道,Dubbo使用默认的网络传输框架是Netty,服务提供方NettyServer使用两个NIO线程池EventLoopGroup(boss)和EventLoopGroup(worker),前者负责接收客户端的连接,并分发给后者处理。我们把这两个线程组成为I/O线程(或Netty线程)。
如果服务提供方能快速处理客户端的请求,那么直接在I/O线程(Netty线程)上处理比较合适。否则(如查DB、请求三方接口等),需要将客户端的请求,分派(Dispatcher)给业务线程池(Dubbo线程池),我们称之为I/O线程与业务线程分离。
如果大家对Reactor或Netty的线程模型不够熟悉的话,推荐阅读深入理解Netty线程模型。
下面的内容,我为大家深入讲解Dubbo线程模型与线程池策略等相关内容,通过本章内容,大家可以学到如下知识点:
-
Dubbo线程模型分类
-
Dubbo线程模型源码分析
-
Dubbo线程模型的确认时机
-
如何自定义线程模型
Dubbo线程模型分类
Dubbo提供了如下几种线程模型:
1、all(Dubbo默认),对应于AllDispatcher类,该线程模型表示,所有的消息事件都分派给业务线程池,包括请求响应、连接事件、断开连接事件、心跳事件等。
2、direct,对应于DirectDispatcher类,该线程模型表示,所有消息事件都不派发到业务线程池,全部在I/O线程池上处理。
3、message,对应于MessageOnlyDispatcher类,该线程模型表示,只有请求和响应事件分派给业务线程池,其他的事件如连接事件、心跳事件等,直接在I/O线程池中处理。
4、execution,对应于ExecutionDispatcher类,该线程模型表示,只把请求事件分派给业务线程池,其他事件如心跳、连接、响应等直接在I/O线程池中执行
5、connection,对应于ConnectionOrderedDispatcher类,该线程模型表示,将连接事件、断开连接事件等放入一个队列有序等I/O线程池执行,其他类型事件直接分派给业务线程池执行。
Dubbo线程模型源码分析
首先,我们看一下Dispatcher接口,这个接口是所有线程模型需要实现的接口,代码如下
@SPI(AllDispatcher.NAME)
public interface Dispatcher {
@Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"})
ChannelHandler dispatch(ChannelHandler handler, URL url);
}
我们可以看到,这个接口只有一个dispatch方法,并且是一个SPI的扩展点(如果大家对Dubbo的SPI机制不熟悉的话,请务必熟悉该机制,推荐阅读Apache Dubbo系列:增强SPI),并且默认的线程模型是all,即AllDispatcher类,接下来我们分析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);
}
}
public class AllChannelHandler extends WrappedChannelHandler {
public AllChannelHandler(ChannelHandler handler, URL url) {
super(handler, url);
}
@Override
public void connected(Channel channel) throws RemotingException {
ExecutorService cexecutor = getExecutorService();
try {
// 所有事件都分派给业务线程池执行
cexecutor.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 {
// ...
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
// ...
}
@Override
public void caught(Channel channel, Throwable exception) throws RemotingException {
// ...
}
}
AllDispatcher的dispatch方法,直接返回了一个AllChannelHandler的实例,而AllChannelHandler中的所有方法,连接事件、断开连接事件、请求响应事件、异常处理事件,全部交给业务线程处理。
DirectDispatcher类
public class DirectDispatcher implements Dispatcher {
public static final String NAME = "direct";
@Override
public ChannelHandler dispatch(ChannelHandler handler, URL url) {
return handler;
}
}
由于DirectDispatcher线程模型会把所有的事件全部交给I/O线程池执行,所以代码比较简单,直接返回了handler。
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 cexecutor = getExecutorService();
try {
cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
} catch (Throwable t) {
throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
}
}
}
我们看到,MessageOnlyDispatcher的dispatch方法返回了一个MessageOnlyChannelHandler类,该类只有请求响应事件交给业务线程池处理,其他事件直接在I/O线程池上处理。
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 cexecutor = getExecutorService();
// 只有请求事件,分派给业务线程池处理
if (message instanceof Request) {
try {
cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
} catch (Throwable t) {
// ...
}
} else {
handler.received(channel, message);
}
}
}
通过源码可以看到,ExecutionDispatcher的dispatch方法,返回了一个ExecutionChannelHandler,在ExcutionChannelHandler里,只有请求事件分派给业务线程池处理。
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;
// 在构造方法里获取URL参数,创建一个java线程池
public ConnectionOrderedChannelHandler(ChannelHandler handler, URL url) {
super(handler, url);
String threadName = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
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)
);
queuewarninglimit = url.getParameter(Constants.CONNECT_QUEUE_WARNING_SIZE, Constants.DEFAULT_CONNECT_QUEUE_WARNING_SIZE);
}
// 连接事件交给java线程池处理
@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);
}
}
// 断开连接事件交给java线程池处理
@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);
}
}
// 请求响应事件交给I/O线程池处理
@Override
public void received(Channel channel, Object message) throws RemotingException {
ExecutorService cexecutor = getExecutorService();
try {
cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
} catch (Throwable t) {
// ...
}
}
@Override
public void caught(Channel channel, Throwable exception) throws RemotingException {
ExecutorService cexecutor = getExecutorService();
try {
cexecutor.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线程池,而这个线程池是JAVA线程池。并且将连接事件和断开连接事件分派给connectionExecutor线程池处理,而请求响应直接在I/O线程池中处理。
Dubbo线程模型的确认时机
既然Dubbo为我们提供了这么多的线程模型,那框架是在什么时候确认使用哪种线程模型呢?Dubbo的网络传输框架是Netty,服务提供方在启动NettyServer的时候,其构造方法如下:
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
主要是在ChannelHandlers.wrap方法中完成的,我们继续跟进
public static ChannelHandler wrap(ChannelHandler handler, URL url) {
return ChannelHandlers.getInstance().wrapInternal(handler, url);
}
protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
// Dubbo SPI机制,获取自适应扩展类
return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
.getAdaptiveExtension().dispatch(handler, url)));
}
最好是通过Dubbo的自适应扩展机制,根据URL参数来获取具体的线程模型。所以各位,Dubbo SPI机制贯穿了整个Dubbo框架,请大家务必掌握Dubbo SPI机制。Apache Dubbo系列:增强SPI。
如何自定义线程模型
Dubbo SPI机制,使整个框架拥有足够的灵活性,那么我们怎么定义自己的线程模型呢?与JAVA SPI机制类似,实现接口,并在META-INF/dubbo目录创建org.apache.dubbo.remoting.Dispatcher文件,文件的内容为mydispatcher=com.xxx.MyDispatcher,并在相应的配置文件中修改配置,将线程模型修改为自定义的线程模型即可。伪代码如下:
// 实现Dispatcher接口,覆写dispatch方法
class MyDisPatcher implements Dispatcher{
@Override
public ChannelHandler dispatch(ChannelHandler handler, URL url) {
return new MyChannelHandler(handler,url);
}
}
// 继承WrappedChannelHandler,实现相应的方法
class MyChannelHandler extends WrappedChannelHandler{
public MyChannelHandler(ChannelHandler handler, URL url) {
super(handler, url);
}
@Override
public void connected(Channel channel) throws RemotingException {
super.connected(channel);
}
}
好了,今天我为大家带来的分享就是这些,如果有疑问或不恰当的地方,请指出,谢谢。
推介阅读:
Apache Dubbo系列:Netty与Dubbo是如何对接的
本文深入解析了Dubbo的线程模型,包括All、Direct、MessageOnly、Execution和ConnectionOrdered五种模型,分析了它们的处理方式和应用场景。重点讨论了各种模型下事件如何在I/O线程和业务线程池之间分派,以及如何通过自定义线程模型来适应不同的服务处理需求。
6718

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



