美文网首页
dubbo源码解读分析netty的应用

dubbo源码解读分析netty的应用

作者: 南宋临安府 | 来源:发表于2018-03-10 20:44 被阅读0次

    “持而盈之,不如其已。
    揣而锐之,不可常保。
    金玉满堂,莫之能守;富贵而骄,自遗其咎。
    功遂身退,天之道也。”[1]

    上一篇中我们简单分析了netty在dubbo中的应用,本篇从dubbo发布服务的角度从源码中详细分析netty的具体使用。
    开启netty服务,代码的调用链如下:

    ServiceBean
          -->onApplicationEvent()
          -->ServiceConfig.export()
              -->doExport()
                  -->doExportUrls()
                      -->doExportUrlsFor1Protocol()
    

    发布服务的入口代码:

    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
    Exporter<?> exporter = protocol.export(wrapperInvoker);
    

    这里proxyFactory对象默认当做JdkProxyFactory,进入到getInvoker方法。第一个参数是接口的实现对象,第二个参数是即将发布的接口Class,第三个参数是发布协议的URL。

        public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
            return new AbstractProxyInvoker<T>(proxy, type, url) {
                @Override
                protected Object doInvoke(T proxy, String methodName,
                                          Class<?>[] parameterTypes,
                                          Object[] arguments) throws Throwable {
                    Method method = proxy.getClass().getMethod(methodName, parameterTypes);
                    return method.invoke(proxy, arguments);
                }
            };
        }
    

    方法内代码,新建了一个抽象类AbstractProxyInvoker,并实现了抽象方法doInvoke。doInvoke中通过反射机制执行要调用的方法。
    接下来:Exporter<?> exporter = protocol.export(invoker);代码 。这里protocol默认当做是DubboProtocol类。

       public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
            URL url = invoker.getUrl();
    
            // export service.
            String key = serviceKey(url);
            DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
            exporterMap.put(key, exporter);
    
            //export an stub service for dispatching event
            Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
            Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
            if (isStubSupportEvent && !isCallbackservice) {
                String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
                if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
                                "], has set stubproxy support event ,but no stub methods founded."));
                    }
                } else {
                    stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
                }
            }
    
            openServer(url);
            optimizeSerialization(url);
            return exporter;
        }
    

    这个方法将传入的Invoker对象封装到DubboExporter对象中,并生成了唯一的key值。同时将key与DubboExporter对象关联保存进入exporterMap中,它是一个支持高并发的ConcurrentHashMap类。当客户端做远程请求服务时,就是根据key值从这个MAP中取出的真正接口实现对象来响应客户端的请求。在后面的代码分析中会体现出来。

        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(Constants.IS_SERVER_KEY, true);
            if (isServer) {
                ExchangeServer server = serverMap.get(key);
                if (server == null) {
                    serverMap.put(key, createServer(url));
                } else {
                    // server supports reset, use together with override
                    server.reset(url);
                }
            }
        }
    

    首先判断serverMap中是否已经包含了当前服务的ExchangeServer对象,如果没有调用createServer(url)创建一个并保存到serverMap中。继续跟进到createServer中,在这里调用了Exchangers类的静态方法bind创建了一个ExchangeServer对象,并返回出去了。注意bind方法的两个参数,第一个是URL很熟悉对吧!就不细说了,关键是第二个参数requestHandler,它是ExchangeHandlerAdapter类。它重写了很多父接口中的方法。里面重写了一个received方法,这个就是netty框架在接收到客户端请求以后响应处理的入口。具体处理细节在后面分析。这里继续往下看是怎么启动netty服务的。

       private ExchangeServer createServer(URL url) {
            // send readonly event when server closes, it's enabled by default
            url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
            // enable heartbeat by default
            url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
            String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
    
            if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
                throw new RpcException("Unsupported server type: " + str + ", url: " + url);
    
            url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
            ExchangeServer server;
            try {
                server = Exchangers.bind(url, requestHandler);
            } catch (RemotingException e) {
                throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
            }
            str = url.getParameter(Constants.CLIENT_KEY);
            if (str != null && str.length() > 0) {
                Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
                if (!supportedTypes.contains(str)) {
                    throw new RpcException("Unsupported client type: " + str);
                }
            }
            return server;
        }
    
    

    以下是Exchangers类的静态方法bind的所有处理,getExchanger方法最终返回了HeaderExchanger对象。

        public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
            if (url == null) {
                throw new IllegalArgumentException("url == null");
            }
            if (handler == null) {
                throw new IllegalArgumentException("handler == null");
            }
            url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
            return getExchanger(url).bind(url, handler);
        }
        public static Exchanger getExchanger(URL url) {
            String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
            return getExchanger(type);
        }
        public static Exchanger getExchanger(String type) {
            return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
        }
    

    HeaderExchanger类中的bind方法代码如下,将dubbo协议的handler对象最终包装成了DecodeHandler对象,并传入到了Transporters类的bind方法中。

    public class HeaderExchanger implements Exchanger {
    
        public static final String NAME = "header";
    
        public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
            return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
        }
    
        public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
            return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
        }
    
    }
    

    继续跟进Transporters类的bind方法如下。总体思路就是获取Transporter接口的具体实现类,然后调用该实现的bind方法。它有MinaTransporter,NettyTransporter,GrizzlyTransporter三种实现类,这里我就默认使用实现类NettyTransporter了。

        public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
            if (url == null) {
                throw new IllegalArgumentException("url == null");
            }
            if (handlers == null || handlers.length == 0) {
                throw new IllegalArgumentException("handlers == null");
            }
            ChannelHandler handler;
            if (handlers.length == 1) {
                handler = handlers[0];
            } else {
                handler = new ChannelHandlerDispatcher(handlers);
            }
            return getTransporter().bind(url, handler);
        }
    
    public class NettyTransporter implements Transporter {
    
        public static final String NAME = "netty";
    
        public Server bind(URL url, ChannelHandler listener) throws RemotingException {
            return new NettyServer(url, listener);
        }
    
        public Client connect(URL url, ChannelHandler listener) throws RemotingException {
            return new NettyClient(url, listener);
        }
    
    }
    

    NettyTransporter类的源码如下。关注下bind方法,新建了一个NettyServer对象。感觉离netty越来越近了。
    在NettyServer类中重点关注两个方法doOpen(它重写了抽象类父类的AbstractServer中的doOpen抽象方法)和它的构造函数。

        public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
            super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
        }
        protected void doOpen() throws Throwable {
            NettyHelper.setNettyLoggerFactory();
    
            bootstrap = new ServerBootstrap();
    
            bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
            workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
                    new DefaultThreadFactory("NettyServerWorker", true));
    
            final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
            channels = nettyServerHandler.getChannels();
    
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                    .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                            ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
                                    .addLast("decoder", adapter.getDecoder())
                                    .addLast("encoder", adapter.getEncoder())
                                    .addLast("handler", nettyServerHandler);
                        }
                    });
            // bind
            ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
            channelFuture.syncUninterruptibly();
            channel = channelFuture.channel();
    
        }
    

    构造函数跟进super方法的处理。查看父类AbstractServer中的构造函数,细看里面执行了doOpen方法,根据抽象模版方法模式,其实调用的是子类的doOpen方法。到此已经将netty服务开启。


    1. 老子《道德经》第九章,老子故里,中国鹿邑。

    相关文章

      网友评论

          本文标题:dubbo源码解读分析netty的应用

          本文链接:https://www.haomeiwen.com/subject/ammbfftx.html