一、背景

Dubbo 将底层通信框架中接收请求的线程称为 IO 线程。如果一些事件处理逻辑可以很快执行完,比如只在内存打一个标记,此时直接在 IO 线程上执行该段逻辑即可。但如果事件的处理逻辑比较耗时,比如该段逻辑会发起数据库查询或者 HTTP 请求。此时我们就不应该让事件处理逻辑在 IO 线程上执行,而是应该派发到线程池中去执行。原因也很简单,IO 线程主要用于接收请求,如果 IO 线程被占满,将导致它不能接收新的请求。

二、源码分析

首先通过 一张Dubbo一次请求大致流程图,看一下线程派发器所处的位置。

spring dubbo 配置 线程 dubbo io线程_线程池

如上图,红框中的 Dispatcher 就是线程派发器。需要说明的是,Dispatcher 真实的职责创建具有线程派发能力的 ChannelHandler,比如 AllChannelHandler、MessageOnlyChannelHandler 和 ExecutionChannelHandler 等,其本身并不具备线程派发能力。Dubbo 支持 5 种不同的线程派发策略,下面通过一个表格列举一下。

spring dubbo 配置 线程 dubbo io线程_Dubbo源码_02

默认配置下,Dubbo 使用 all 派发策略,即将所有的消息都派发到线程池中。下面我们来分析一下 AllChannelHandler 的代码。

/**
* @Author: wenyixicodedog
* @Date:  2020-07-09
* @Param:
* @return:
* @Description:  所有消息都派发到线程池,包括请求,响应,连接事件,断开事件等
*/
public class AllChannelHandler extends WrappedChannelHandler {

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

    /** 处理连接事件 */
    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);
        }
    }

    /** 处理断开事件 */
    public void disconnected(Channel channel) throws RemotingException {
        ExecutorService cexecutor = getExecutorService();
        try {
            cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.DISCONNECTED));
        } catch (Throwable t) {
            throw new ExecutionException("disconnect event", channel, getClass() + " error when process disconnected event .", t);
        }
    }

    /** 处理请求和响应消息,这里的 message 变量类型可能是 Request,也可能是 Response */
    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) {
            // 如果通信方式为双向通信,此时将 Server side ... threadpool is exhausted
            // 错误信息封装到 Response 中,并返回给服务消费方。
          if(message instanceof Request && t instanceof RejectedExecutionException){
            Request request = (Request)message;
            if(request.isTwoWay()){
              String msg = "Server side(" + url.getIp() + "," + url.getPort() + ") threadpool is exhausted ,detail msg:" + t.getMessage();
              Response response = new Response(request.getId(), request.getVersion());
              response.setStatus(Response.SERVER_THREADPOOL_EXHAUSTED_ERROR);
              response.setErrorMessage(msg);
                    // 返回包含错误信息的 Response 对象
              channel.send(response);
              return;
            }
          }
            throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
        }
    }

    //服务端获取线程池
    private ExecutorService getExecutorService() {
        ExecutorService cexecutor = executor;
        if (cexecutor == null || cexecutor.isShutdown()) {
            cexecutor = SHARED_EXECUTOR;
        }
        return cexecutor;
    }
}

如上,connected主要负责请求、响应的发起和接收,received方法主要负责请求或者相应的处理业务。先大致描述下有关AllChannelHandler在这一部分的一次请求流程:client发起请求connected->server接收请求connected->nettyHandler.receive方法接收请求->AllChannelHandler#receive处理请求->server发起响应请求connected->client接收请求connected。

ChannelState.CONNECTED、ChannelState.RECEIVED都是利用枚举声明的请求状态。

spring dubbo 配置 线程 dubbo io线程_Dubbo源码_03

另外,AllChannelHandler#getExecutorService用来获取线程池,这个首先通过SPI技术获取线程池,如果为空则赋值一个shared_executor,这部分稍等再细看下。->⭐️⭐️

回到AllChannelHandler,获取连接之后将连接事件交给线程池处理,请求对象会被封装 ChannelEventRunnable 中,ChannelEventRunnable 将会是服务调用过程的新起点。所以接下来我们以 ChannelEventRunnable 为起点向下探索。

该类的主要代码如下:

/**
* @Author: wenyixicodedog
* @Date:  2020-07-09
* @Param:
* @return:
* @Description:  开启线程执行业务逻辑
* 请求对象会被封装 ChannelEventRunnable 中,ChannelEventRunnable 将会是服务端调用过程的新起点。
*/
public class ChannelEventRunnable implements Runnable {

    private static final Logger logger = LoggerFactory.getLogger(ChannelEventRunnable.class);

    private final ChannelHandler handler;
    private final Channel channel;
    private final ChannelState state;
    private final Throwable exception;
    private final Object message;

    。。。

    public ChannelEventRunnable(Channel channel, ChannelHandler handler, ChannelState state, Object message, Throwable exception) {
        this.channel = channel;
        this.handler = handler;
        this.state = state;
        this.message = message;
        this.exception = exception;
    }

    /**
    * @Author: wenyixicodedog
    * @Date:  2020-07-09
    * @Param:
    * @return:
    * @Description:  ChannelEventRunnable 仅是一个中转站,它的 run 方法中并不包含具体的调用逻辑,
     * 仅用于将参数传给其他 ChannelHandler 对象进行处理,该对象类型为 DecodeHandler。
    */
    public void run() {
        // 检测通道状态,对于请求或响应消息,此时 state = RECEIVED
        switch (state) {
            case CONNECTED:
                try {
                    handler.connected(channel);
                } catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e);
                }
                break;
            case DISCONNECTED:
                try {
                    handler.disconnected(channel);
                } catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel, e);
                }
                break;
            case SENT:
                try {
                    handler.sent(channel, message);
                } catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
                            + ", message is " + message, e);
                }
                break;
            //请求和响应消息出现频率明显比其他类型消息高,所以新版dubbo对RECEIVED单独拿出来作为一个处理方式放在switch前面。
            case RECEIVED:
                try {
                    handler.received(channel, message);
                } catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
                            + ", message is " + message, e);
                }
                break;
            case CAUGHT:
                try {
                    handler.caught(channel, exception);
                } catch (Exception e) {
                    logger.warn("ChannelEventRunnable handle " + state + " operation error, channel is " + channel
                            + ", message is: " + message + ", exception is " + exception, e);
                }
                break;
            default:
                logger.warn("unknown state: " + state + ", message is " + message);
        }
    }

}

因为ChannelEventRunnable实现了 Runnable 接口,所以他的核心逻辑就是run方法执行的内容。请求和响应消息出现频率明显比其他类型消息高,所以新版本的dubbo这里对该类型的消息进行了针对性判断,我的dubbo是2.5.x,所以暂时还是放在了一起进行判断。ChannelEventRunnable 其实仅仅是一个中转站,它的 run 方法中并不包含具体的调用逻辑,仅用于将参数传给其他 ChannelHandler 对象进行处理,该对象类型为 DecodeHandler。这里先看一下他们各个类之间的关系。

spring dubbo 配置 线程 dubbo io线程_Dubbo线程池_04

WrapperChannelHandler负责的是事件派发,AbstractChannelHandlerDelegate的子类负责解码、心跳相关操作,HeaderExchangeHandler的子类负责后续请求的处理。

我们进入到DecodeHandler

public class DecodeHandler extends AbstractChannelHandlerDelegate {

    private static final Logger log = LoggerFactory.getLogger(DecodeHandler.class);

    public DecodeHandler(ChannelHandler handler) {
        super(handler);
    }

    public void received(Channel channel, Object message) throws RemotingException {
        if (message instanceof Decodeable) {
            // 对 Decodeable 接口实现类对象进行解码
            decode(message);
        }

        if (message instanceof Request) {
            // 对 Request 的 data 字段进行解码
            decode(((Request) message).getData());
        }

        if (message instanceof Response) {
            // 对 Response 的 result 字段进行解码
            decode(((Response) message).getResult());
        }
        // 执行后续逻辑
        handler.received(channel, message);
    }

    private void decode(Object message) {
        // Decodeable 接口目前有两个实现类,
        // 分别为 DecodeableRpcInvocation 和 DecodeableRpcResult
        if (message != null && message instanceof Decodeable) {
            try {
                // 执行解码逻辑
                ((Decodeable) message).decode();
                if (log.isDebugEnabled()) {
                    log.debug(new StringBuilder(32).append("Decode decodeable message ")
                            .append(message.getClass().getName()).toString());
                }
            } catch (Throwable e) {
                if (log.isWarnEnabled()) {
                    log.warn(
                            new StringBuilder(32)
                                    .append("Call Decodeable.decode failed: ")
                                    .append(e.getMessage()).toString(),
                            e);
                }
            } 
        } 
    } 

}

DecodeHandler 主要是包含了一些解码逻辑。请求解码可在 IO 线程上执行,也可在线程池中执行,这个取决于运行时配置。DecodeHandler 存在的意义就是保证请求或响应对象可在线程池中被解码。解码完毕后,完全解码后的 Request 对象会继续向后传递,下一站是 HeaderExchangeHandler。

/**
 * 交换接收器
 */
public class HeaderExchangeHandler implements ChannelHandlerDelegate {

    protected static final Logger logger = LoggerFactory.getLogger(HeaderExchangeHandler.class);

    public static String KEY_READ_TIMESTAMP = HeartbeatHandler.KEY_READ_TIMESTAMP;

    public static String KEY_WRITE_TIMESTAMP = HeartbeatHandler.KEY_WRITE_TIMESTAMP;

    private final ExchangeHandler handler;

    public HeaderExchangeHandler(ExchangeHandler handler) {
        if (handler == null) {
            throw new IllegalArgumentException("handler == null");
        }
        this.handler = handler;
    }

  ......
  
    Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
        Response res = new Response(req.getId(), req.getVersion());
          // 检测请求是否合法,不合法则返回状态码为 BAD_REQUEST 的响应
          if (req.isBroken()) {
            Object data = req.getData();

            String msg;
              if (data == null) msg = null;
              else if (data instanceof Throwable) msg = StringUtils.toString((Throwable) data);
              else msg = data.toString();
            res.setErrorMessage("Fail to decode request due to: " + msg);
              // 设置 BAD_REQUEST 状态
            res.setStatus(Response.BAD_REQUEST);

              return res;
        }
          // 获取 data 字段值,也就是 RpcInvocation 对象
        Object msg = req.getData();
          try {
              // 继续向下调用
            Object result = handler.reply(channel, msg);
              // 设置 OK 状态码
            res.setStatus(Response.OK);
              // 设置调用结果
            res.setResult(result);
        } catch (Throwable e) {
              // 若调用过程出现异常,则设置 SERVICE_ERROR,表示服务端异常
            res.setStatus(Response.SERVICE_ERROR);
            res.setErrorMessage(StringUtils.toString(e));
        }
          return res;
    }

      public void received(Channel channel, Object message) throws RemotingException {
        channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
        ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
        try {  
              // 处理请求对象
              if (message instanceof Request) {
                  // handle request.
                Request request = (Request) message;
                  // 处理事件
                  if (request.isEvent()) {
                    handlerEvent(channel, request);
                } else {
                      // TODO  双向通信
                      if (request.isTwoWay()) {
                          // TODO  向后调用服务,并得到调用结果
                        Response response = handleRequest(exchangeChannel, request);
                          // TODO  将调用结果返回给服务消费端
                        channel.send(response);
                    } else {
                          // TODO  如果是单向通信,仅向后调用指定服务即可,无需返回调用结果
                        handler.received(exchangeChannel, request.getData());
                    }
                }
              // 处理响应对象,服务消费方会执行此处逻辑,后面分析
            } else if (message instanceof Response) {
                handleResponse(channel, (Response) message);
              // telnet 相关,忽略
            } else if (message instanceof String) {
                  if (isClientSide(channel)) {
                    Exception e = new Exception("Dubbo client can not supported string message: " + message + " in channel: " + channel + ", url: " + channel.getUrl());
                    logger.error(e.getMessage(), e);
                } else {
                    String echo = handler.telnet(channel, (String) message);
                      if (echo != null && echo.length() > 0) {
                        channel.send(echo);
                    }
                }
            } else {
                handler.received(exchangeChannel, message);
            }
        } finally {
            HeaderExchangeChannel.removeChannelIfDisconnected(channel);
        }
    }
    ......
}

到这里,我们看到了比较清晰的请求和响应逻辑。对于双向通信,HeaderExchangeHandler 首先向后进行调用,得到调用结果。然后将调用结果封装到 Response 对象中,最后再将该对象返回给服务消费方。如果请求不合法,或者调用失败,则将错误信息封装到 Response 对象中,并返回给服务消费方。否则,获取 data 字段值,也就是 RpcInvocation 对象,继续进行向下调用。

三、服务端接口调用

接下来我们继续向后分析,看一下我们的手写的实现类最终调用的流程是什么样的。从HeaderExchangeHandler#handleRequest#reply继续往下走。

spring dubbo 配置 线程 dubbo io线程_Dubbo源码_05

来到定义在 DubboProtocol 类中的匿名类对象逻辑,如下:

//创建匿名实现类实现reply方法
    private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {

        // TODO 从HeaderExchangeHandler#handleRequest#Object result = handler.reply(channel, msg);调用进来
        public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
            if (message instanceof Invocation) {
                Invocation inv = (Invocation) message;
                //根据客户端请求信息封装key值从exporterMap取出接口实现类对象invoker 获取 Invoker 实例
                Invoker<?> invoker = getInvoker(channel, inv);
                // 如果是回调,则需要考虑向后兼容性
                if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
                    String methodsStr = invoker.getUrl().getParameters().get("methods");
                    boolean hasMethod = false;
                    if (methodsStr == null || methodsStr.indexOf(",") == -1) {
                        hasMethod = inv.getMethodName().equals(methodsStr);
                    } else {
                        //循环验证客户端请求的方法,服务端是否存在对应的实现
                        String[] methods = methodsStr.split(",");
                        for (String method : methods) {
                            if (inv.getMethodName().equals(method)) {
                                hasMethod = true;
                                break;
                            }
                        }
                    }
                    //不存在直接抛出异常
                    if (!hasMethod) {
                        logger.warn(new IllegalStateException("The methodName " + inv.getMethodName() + " not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) + " ,invocation is :" + inv);
                        return null;
                    }
                }
                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
                //利用反射调用实现类对象的方法响应请求
                return invoker.invoke(inv);
            }
            throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
        }

        /**
        * @Author: wenyixicodedog
        * @Date:  2020-07-03
        * @Param:  [channel, message]
        * @return:  void
        * @Description:  接受客户端请求并返回响应消息
        */
        @Override
        public void received(Channel channel, Object message) throws RemotingException {
            if (message instanceof Invocation) {
                //服务端处理并返回响应消息
                reply((ExchangeChannel) channel, message);
            } else {
                super.received(channel, message);
            }
        }

        @Override
        public void connected(Channel channel) throws RemotingException {
            invoke(channel, Constants.ON_CONNECT_KEY);
        }
        ......
    };

      首先根据客户端请求信息封装key值从exporterMap取出接口实现类对象invoker 获取 Invoker 实例,并通过 Invoker 的 invoke 方法调用服务逻辑。invoke 方法定义在 AbstractProxyInvoker 中,代码如下。

首先看下getInvoker逻辑

spring dubbo 配置 线程 dubbo io线程_spring dubbo 配置 线程_06

核心逻辑就是计算service key,然后从exporterMap缓存中查找与serviceKey相对应的 DubboExporter 对象,exporterMap是exporter的缓存,这个在服务暴露通过export方法将wrapperInvoker暴露的时候会获取到exporter,然后将 <serviceKey, DubboExporter> 映射关系存储到 exporterMap 集合中。

        当然,DubboProtocol#reply方法执行完了还要进行一些过滤器链的调用,比如监视器monitor、trace等,就不细看啦。可以自己debug分析。

对于invoker.invoke调用,invoke 方法定义在 AbstractProxyInvoker 中,代码如下。

public abstract class AbstractProxyInvoker<T> implements Invoker<T> {

    ......

    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        try {
            // 调用 doInvoke 执行后续的调用,并将调用结果封装到 RpcResult 中,并
            return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
        } catch (InvocationTargetException e) {
            return new RpcResult(e.getTargetException());
        } catch (Throwable e) {
            throw new RpcException("Failed to invoke remote proxy method ...");
        }
    }

    protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;
}

如上,invoke方法进行doInvoke的调用,然后将结果封装在了RPCResult对象之中。doInvoke 是一个抽象方法,这个需要由具体的 Invoker 实例实现。

spring dubbo 配置 线程 dubbo io线程_Dubbo线程池_07

这个Invoker 实例是在运行时通过 JavassistProxyFactory 中以内部类的方式创建的,创建逻辑如下:

spring dubbo 配置 线程 dubbo io线程_线程池_08

Wrapper 是一个抽象类,其中 invokeMethod 是一个抽象方法。Dubbo 会在运行时通过 Javassist 框架为 Wrapper 生成实现类,并实现 invokeMethod 方法,该方法最终会根据调用信息调用具体的服务。以 DemoServiceImpl 为例,Javassist 为其生成的代理类如下。

public class Wrapper0 extends Wrapper implements ClassGenerator.DC {
    public static String[] pns;
    public static Map pts;
    public static String[] mns;
    public static String[] dmns;
    public static Class[] mts0;

    // 省略其他方法

    public Object invokeMethod(Object object, String string, Class[] arrclass, Object[] arrobject) throws InvocationTargetException {
        DemoService demoService;
        try {
            // 类型转换
            demoService = (DemoService)object;
        }
        catch (Throwable throwable) {
            throw new IllegalArgumentException(throwable);
        }
        try {
            // 根据方法名调用指定的方法
            if ("sayHello".equals(string) && arrclass.length == 1) {
                return demoService.sayHello((String)arrobject[0]);
            }
        }
        catch (Throwable throwable) {
            throw new InvocationTargetException(throwable);
        }
        throw new NoSuchMethodException(new StringBuffer().append("Not found method \"").append(string).append("\" in class com.alibaba.dubbo.demo.DemoService.").toString());
    }
}

到这里,整个服务调用过程就分析完了。

四、AllChannelHandler#getExecutorService

还记得之前说过AllChannelHandler#getExecutorService用来服务端获取线程池,先看下大致逻辑结构。

private ExecutorService getExecutorService() {
        ExecutorService cexecutor = executor;
        if (cexecutor == null || cexecutor.isShutdown()) {
            cexecutor = SHARED_EXECUTOR;
        }
        return cexecutor;
    }
public class WrappedChannelHandler implements ChannelHandlerDelegate {

    protected static final Logger logger = LoggerFactory.getLogger(WrappedChannelHandler.class);

    protected static final ExecutorService SHARED_EXECUTOR = Executors.newCachedThreadPool(new NamedThreadFactory("DubboSharedHandler", true));

    protected final ExecutorService executor;

    protected final ChannelHandler handler;

    protected final URL url;

    public WrappedChannelHandler(ChannelHandler handler, URL url) {
        this.handler = handler;
        this.url = url;
        executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);

        ......        
    }

    ......

}

默认的executor是WrappedChannelHandler构造中dubbo通过SPI从url中获取线程池参数然后动态加载进来的,而shared_executor则是作为成员变量通过SPI加载。我的目的就是想搞明白服务端处理消费端请求使用的线程池模型。

spring dubbo 配置 线程 dubbo io线程_线程池_09

项目启动进行访问,日志显示线程名称为DubboServerHandler

首先在AllChannelHandler#getExecutorService打断点不断进行debug

spring dubbo 配置 线程 dubbo io线程_Dubbo源码_10

找到threadFactory#mPrefix为DubboServerHandler

spring dubbo 配置 线程 dubbo io线程_Dubbo线程池_11

这个时候executor发现被赋值,说明是在之前已经初始化完成。

然后在WrappedChannelHandler#WrappedChannelHandler#executor断点

spring dubbo 配置 线程 dubbo io线程_事件派发线程模型_12

程序执行到@WrapperChannelHandler构造方法,然后getAdaptiveExtension进入动态生成的线程池类ThreadPool$Adaptive

这里我生成了一个ThreadPool运行时的动态类。

/**
 * @author: wenyixicodedog
 * @create: 2020-07-09
 * @description:
 */
public class ThreadPool$Adaptive implements com.alibaba.dubbo.common.threadpool.ThreadPool {

    public java.util.concurrent.Executor getExecutor(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg0;
        String extName = url.getParameter("threadpool", "fixed");
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.common.threadpool.ThreadPool) name from url" +
                    "(" + url.toString() + ") use keys([threadpool])");
        com.alibaba.dubbo.common.threadpool.ThreadPool extension =
                ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.threadpool.ThreadPool.class).getExtension(extName);
        return extension.getExecutor(arg0);
    }
}

因为根据url获取到的threadpool为空所以用的是默认配置的fixed

spring dubbo 配置 线程 dubbo io线程_Dubbo源码_13

然后getExecutor就是进入到fixedThreadPool的执行逻辑了

spring dubbo 配置 线程 dubbo io线程_事件派发线程模型_14

NamedThreadFactory该线程工厂为我们的线程池创建了名称,为线程创建了名称,方便我们查看堆栈信息时进行 debug ,线程为守护/用户的决定权由使用者决定。

队列容量queues默认为0,该线程池的workQueue为SynchronousQueue,queues如果小于0,使用无界阻塞队列LinkedBlockingQueue,queues如果大于0,使用以queues为capacity的有界阻塞队列LinkedBlockingQueue。

queues默认为0,workQueue为SynchronousQueue,但是阻塞队列虽然默认是 SynchronousQueue,如果用户配置了 queues 变量,且其值较大,使用的阻塞队列就是 LinkedBlockingQueue,此时一旦 core 配置较小,就会导致事件阻塞。

我的dubbo版本为2.5.x,在Dubbo 的最新源码中又提供了一个新的线程池

EagerThreadPool,当核心线程池核心线程都在忙碌的时候,新来的任务不是放在workQueue当中,而是新开启线程进行执行任务。dubbo的线程池好像在 java.util.concurrent.Executors 都为我们提供了。只不过 Dubbo 又自己实现了一下,设定了下自定义参数。

dubbo线程池选择可以自定义配置

<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="200"/>

dubbo的优化很多可以针对线程池入手,有关dubbo线程池详细内容后续在进行详细分析吧。😜

这里再来回顾下Executor框架中FixedThreadPool和CachedThreadPool特性作为结束。【Java并发编程的艺术】

①、FixedThreadPool

spring dubbo 配置 线程 dubbo io线程_线程池_15

FixedThreadPool:称为可重用固定线程数的线程池。

FixedThreadPool的corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads。

当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止。如果把keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止。

FixedThreadPool使用无界阻塞队列LinkedBlockingQueue作为线程池的工作队列(队列的容量为Integer.MAX_VALUE)。使用无界阻塞队列作为工作队列会对线程池带来如下影响。

1)当线程池中的线程数达到corePoolSize后,新任务将在队列中等待,因此线程池中的线程数不会超过corePoolSize。

2)由于1,使用无界阻塞队列时maximumPoolSize将是一个无效参数。

3)由于1和2,使用无界阻塞队列时keepAliveTime将是一个无效参数。

4)由于使用无界阻塞队列,运行中的FixedThreadPool(未执行方法shutdown()或shutdownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution方法)。

②、CachedThreadPool

spring dubbo 配置 线程 dubbo io线程_事件派发线程模型_16

CachedThreadPool:是一个会根据需要创建新线程的线程池。

CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但CachedThreadPool的maximumPool是无界的。这意味着,如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新线程。极端情况下,CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源。

spring dubbo 配置 线程 dubbo io线程_线程池_17