总所周知,dubbo是一个RPC框架,其网络通信采用Netty,其Netty服务在何时启动?启动流程是怎样的?线程模型是怎样的的?本文将解答以上问题。
Netty服务启动流程
服务端与消费端的启动流程大同小异,你可以以同样的方法来分析服务端的启动流程,所以这里以服务端为例。
Dubbo中有一个叫做NettyServer的类,该类就是Netty服务启动类。
我们首先看该类的构造方法。
1 public NettyServer(URL url, ChannelHandler handler) throws RemotingException { 2 super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME))); 3 } 4 5 // ChannelHandlers 6 public static ChannelHandler wrap(ChannelHandler handler, URL url) { 7 return ChannelHandlers.getInstance().wrapInternal(handler, url); 8 } 9 10 protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) { 11 return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class) 12 .getAdaptiveExtension().dispatch(handler, url))); 13 }
NettyServer是ChannelHandler的包装类,其ChannelHandler接口,又持有了ChannelHandler对象,其中的ChannelHandler的调用链在构造方法中已经完整的呈现出来了。
值得注意的是,在ChannelHandlers#wrapInternal方法中获取了Dispthcer的自适应类,然后调用了它的dispath方法。这里默认会获取到AllDispatcher对象,该对象的dispath方法就会创建一个AllChannelHandler对象。还记得在《服务注册与调用流程》中提到的处理请求、响应的AllChannelHandler对象吗,该对象就是在这里创建的。
接下来我们看下服务启动的入口,该类中有一个doOpen方法,该方法就是服务创建的入口。
1 protected void doOpen() throws Throwable { 2 NettyHelper.setNettyLoggerFactory(); 3 ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true)); 4 ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true)); 5 ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS)); 6 bootstrap = new ServerBootstrap(channelFactory); 7 8 final NettyHandler nettyHandler = new NettyHandler(getUrl(), this); 9 channels = nettyHandler.getChannels(); 10 11 bootstrap.setPipelineFactory(new ChannelPipelineFactory() { 12 @Override 13 public ChannelPipeline getPipeline() { 14 NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this); 15 ChannelPipeline pipeline = Channels.pipeline(); 16 // 添加编解码器以及Dubbo的ChannelHandler 17 pipeline.addLast("decoder", adapter.getDecoder()); 18 pipeline.addLast("encoder", adapter.getEncoder()); 19 pipeline.addLast("handler", nettyHandler); 20 return pipeline; 21 } 22 }); 23 // bind 24 channel = bootstrap.bind(getBindAddress()); 25 }
其流程就是一个普通的Netty服务端的启动流程,这里不赘述。虽然流程简单,但是这里体现了dubbo的线程模型,放到下一节来讲。
我们在该方法打一个断点就能够看到Netty服务的入口是在DubboProtocol的export方法,其中有一行调用了openServer方法,这里就是入口。
线程模型
我们再次看一下doOpen方法,其中值得注意的是,Dubbo采用的Netty线程模型是使用的主从线程模型,然后在AllChannelHandler中也会有一个线程池。
我们在DubboProtocol#openServer中可以看到,对于同一个地址(ip:port)只会存在一个NettyServer,对于服务端来说,地址就是自身的ip+dubbo协议的port,所以AllChannelHandler也会只有一个,所以AllChannelHandler中的线程池是服务端共享的。
通过上面的分析,那么我们可以得出Dubbo的线程模型如下图所示:
boss线程以及worker线程都是Netty的线程,而worker线程中还会使用到一个线程池,该线程池即为Dubbo的业务线程池,也就是AllChannelHandler中使用到的那个线程池。
Dubbo提供了若干种线程分发策略,即Dispather的实现类:
反应到程序中,就是将AllChannelHandler替换成了其他的ChannelHandler。