Apache Dubbo系列:Dubbo线程模型

本文深入解析了Dubbo的线程模型,包括All、Direct、MessageOnly、Execution和ConnectionOrdered五种模型,分析了它们的处理方式和应用场景。重点讨论了各种模型下事件如何在I/O线程和业务线程池之间分派,以及如何通过自定义线程模型来适应不同的服务处理需求。

导读

我们都知道,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系列:ZooKeeper注册中心

Apache Dubbo系列:增强SPI

Apache Dubbo系列:Netty与Dubbo是如何对接的

Apache Dubbo系列:集群容错整体架构

深入理解Netty线程模型

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值