dubbo的provider侧 线程池分析
1.背景
今天看到一个复盘,其中一个问题是接口延时高导致dubbo的线程池被打满,因此好奇dubbo具体的线程池实现。本文重点关注线程池相关的内容,之前梳理过dubbo的connection
管理 dubbo服务连接管理分析,可以相互参考。
2.配置文件的解析器
dubbo的配置是xml的文件,通常是一个dubbo-provider.xml
,一个dubbo-consumer.xml
,解析这两个问题是dubbo运行的第一步,但在这之前是加载对应的配置解析器。
spring的nameSpace解析器:
org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver
对于dubbo而言,起对应的配置内容是:
配置的类为: com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
此处的配置跟dubbo的consumer、provider配置对应
dubbo对应的配置解析类都注册在 DubboNamespaceHandler
中:
其中注册了dubbo配置文件的各种解析类
对于provider,是提供service服务,配置的是<dubbo:service>
,重点关注 com.alibaba.dubbo.config.spring.ServiceBean
对于consumer,是消费service服务,配置的是<dubbo:reference>
,重点关注 com.alibaba.dubbo.config.spring.ReferenceBean
3. provider的invoker初始化
dubbo的provider配置中,每一个<dubbo:service>
标签,都会实例化为一个 Invoker
。
Invoker
的实例化过程从上节提到的ServiceBean
开始:
com.alibaba.dubbo.config.spring.ServiceBean#onApplicationEvent
com.alibaba.dubbo.config.ServiceConfig#doExport
在doExport() 方法中会进行配置检查:
执行导出的过程中,会创建具体的invoker实例:
com.alibaba.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol
创建了 invoker的实例后,最终调用的是 com.alibaba.dubbo.rpc.Protocol#export
方法,不同的协议它都有独立的实现:
对于默认的dubbo协议,最终的实现在 com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#export
重点:此处将invoker封装到exporter后,放到了exporterMap中了
4. 创建线程池
在上述 DubboProtocol#export
方法中,最后执行了一个方法:
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#openServer
该逻辑会执行到 com.alibaba.dubbo.remoting.transport.dispatcher.all.AllDispatcher#dispatch
, 创建 AllChannelHandler
实体
Dispatcher 的不同实现类决定了线程池的策略,具体的配置如下(
META-INF/dubbo.inernal
)
- all:对应 AllDispatcher,用于将所有任务都分发到线程池中。
- direct:对应 DirectDispatcher,用于直接在 I/O 线程处理任务,不使用线程池。
- message:对应 MessageOnlyDispatcher,只处理消息相关的任务。
- execution:对应 ExecutionDispatcher,用于处理执行相关的任务。
- connection:对应 ConnectionOrderedDispatcher,按连接顺序处理任务。
线程池在创建 com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler
实例时就创建了,根据不同的配置有不同的实现
AllChannelHandler 是 WrappedChannelHandler 的子类,线程池默认情况下是固定大小
com.alibaba.dubbo.common.threadpool.support.fixed.FixedThreadPool
,线程数量为200
AllChannelHandler 中管理了线程池,默认情况下都是共享一个线程池,但是支持通过配置给service指定对用的线程池,此时会单独给这个service创建一个 AllChannelHandler 对象
5.provider处理请求的主流程
从netty接收message信息:
com.alibaba.dubbo.remoting.transport.netty.NettyHandler#messageReceived
获取线程池,并将任务提交至线程池:
com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler
具体的任务逻辑定义:
com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable#run
注意: 之后的receive流程都是运行在线程池中
received 方法的执行:
com.alibaba.dubbo.remoting.transport.DecodeHandler#received
com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#received
com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleRequest
com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleRequest
处理请求时的apply方法定义在 com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
中
选择invoker:
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getInvoker
选择对应的invoker就是从 exporterMap 获取,而 exporterMap 中的信息则是在exporter时写入的