4、Dubbo源码系列-服务端Netty启动

399 阅读3分钟

上篇文章从DubboBootstrap start方法一直分析到了RegistryProtocol register方法,其中提到,RegistryProtocol doLocalExport最终会启动Netty服务用来处理客户端的请求,本篇文章,将会详细分析下上述过程。

一、时序图

image.png 老规矩,有图先看图,以上即为RegistryProtocol到NettyTransporter的时序图,最终由NettyTransporter创建NettyServer对象。

二、步骤详解

拿出一些关键的步骤,进行源码分析。

1.步骤2

    private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
        String key = getCacheKey(originInvoker);

        return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
            Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
            return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
        });
    }

可以看到,核心方法则是调用protocol.export方法,看过前文的同学应该明白,此刻的protocol已经是装饰后的Protocol$Adaptive对象了,由于默认用的dubbo协议,经历层层的Wrapper后,最终调用DubboProtocol的export方法

2.步骤7

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        ... ...
        // 启动netty
        openServer(url);
        optimizeSerialization(url);
        // 根据invoker中配置的optimizer参数获取扩展的自定义序列化处理类
        return exporter;
    }

核心方法为openServer方法

3.步骤8

    private void openServer(URL url) {
        // find server.
        String key = url.getAddress();
        //client can export a service which's only for server to invoke
        boolean isServer = url.getParameter(IS_SERVER_KEY, true);
        if (isServer) {
            ProtocolServer server = serverMap.get(key);
            if (server == null) {
                synchronized (this) {
                    server = serverMap.get(key);
                    if (server == null) {
                        serverMap.put(key, createServer(url));
                    }
                }
            } else {
                // server supports reset, use together with override
                server.reset(url);
            }
        }
    }

先获取到当前机器的ip地址,然后判断是否是服务端,如果当前是服务端,则首先会从缓存中获取,获取不到,createServer去创建。

4.步骤13

这里提一句,Transporters对象本身也是个SPI,因此,Dubbo也会为其生成个Adaptive类,默认实现为NettyTransporter

@SPI("netty")
public interface Transporter {
    RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException;
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

5.步骤14

public class NettyTransporter implements Transporter {

    public static final String NAME = "netty3";

    @Override
    public RemotingServer bind(URL url, ChannelHandler listener) throws RemotingException {
        return new NettyServer(url, listener);
    }

    @Override
    public Client connect(URL url, ChannelHandler listener) throws RemotingException {
        return new NettyClient(url, listener);
    }

}

bind方法则是直接new了NettyServer对象

三、NettyServer解析

    protected void doOpen() throws Throwable {
        NettyHelper.setNettyLoggerFactory();
        ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
        ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
        ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
        bootstrap = new ServerBootstrap(channelFactory);

        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
        channels = nettyHandler.getChannels();
        // https://issues.jboss.org/browse/NETTY-365
        // https://issues.jboss.org/browse/NETTY-379
        // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
        bootstrap.setOption("child.tcpNoDelay", true);
        bootstrap.setOption("backlog", getUrl().getPositiveParameter(BACKLOG_KEY, Constants.DEFAULT_BACKLOG));
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            @Override
            public ChannelPipeline getPipeline() {
                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                ChannelPipeline pipeline = Channels.pipeline();
                pipeline.addLast("decoder", adapter.getDecoder());
                pipeline.addLast("encoder", adapter.getEncoder());
                pipeline.addLast("handler", nettyHandler);
                return pipeline;
            }
        });
        // bind
        channel = bootstrap.bind(getBindAddress());
    }
    

NettyServer的核心方法doOpen,标准的Netty server实现,这里可以看到,boss线程和worker线程,使用的都是无界的线程池,毕竟RPC服务,还是要保证服务端的可用性。

指定decoder和encoder以及handler,这里就先不展开讲了,后面分析服务端处理请求的时候会详细分析下其实现。

四、小节

本文章主要分析了从RegistryProtocol一直到Netty启动的过程中的源码设计,可以看到netty部分中最关键的三个组件为decoder和encoder以及handler,后面分析处理客户端请求的时候会着重分析下。本篇其实还遗留了一点,比如netty启动后,是如何把服务注册到注册中心上的,接下来的文章会详细分析下相关设计。