Dubbo服务启动过程(三)

2017-12-05  本文已影响152人  此鱼不得水

在上一节中介绍了Dubbo启动过程中的一个操作:将Dubbo服务注册到Zk上。下面就介绍启动过程的另外一个重要操作:服务监听。

服务监听逻辑听起来是比较简单的。毕竟就四个字~~服务监听这里的逻辑比较绕,看了好久都感觉有点云里雾里的,所以只能尽力去讲一些自己看懂的地方了,有的地方如果自己说错了请直接评论区回复就好。

概念澄清

Channel:Dubbo中的Channel不同于Netty中Channel,也是为了屏蔽底层逻辑而自定义的接口类,比如NettyChannel就是包含了Dubbo中Channel与Netty中的Channel的映射关系。
ChannelHandler:含义类似于Netty中的ChannelHandler,主要是用于触发某些事件,比如connected,disconnectd,received等事件。

装饰者模式:在dubbo的源码中充斥着装饰者模式,在我们看到的Handler出现的时候,往往都是装饰者模式在运作着。Handler内部中嵌套着Handler的情景非常之多,所以理清楚每个Handler都干了什么显得十分重要(麻烦)。

监听端口的过程

从上一章知道DubboProtocol.export()是这次研究的入口,我们就从这里开始看:

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        URL url = invoker.getUrl();
        
        // export service.
        //这个key就是可以唯一区分所暴露的服务的key,group+interface+version
        String key = serviceKey(url);
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        //唯一的服务与exporter映射起来,这样的话在DubboProtocol中就能根据三要素找到具体可以执行的Exporter
        exporterMap.put(key, exporter);
        
        //重点
        openServer(url);
        
        return exporter;
    }
    
    private void openServer(URL url) {
        // find server.
        //host:port
        String key = url.getAddress();
        //client 也可以暴露一个只有server可以调用的服务。
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
        if (isServer) {
            //因为Dubbo是基于长链接的,所以每一个client和server只会通过一个长链接来进行通信,所以这里通过client的key与server作为一个映射存储起来
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
                //重点
                serverMap.put(key, createServer(url));
            } else {
                //server支持reset,配合override功能使用
                server.reset(url);
            }
        }
    }
    
    private ExchangeServer createServer(URL url) {
        //默认开启server关闭时发送readonly事件
        url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
        //默认开启heartbeat
        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, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
        ExchangeServer server;
        try {
            //所以重点就在于对于Server的创建上
            //requestHandler可以理解为对于request的处理器
            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;
    }

从上文的代码中我们看到,最后的重点落到了server = Exchangers.bind(url, requestHandler);上,在这句代码中创建了server并完成了监听。这里也是最初的Handler的原型,即requestHandler。下面我们先理一下Handler都具体做了哪些:

public interface ChannelHandler {

    /**
     * on channel connected.
     * 
     * @param channel channel.
     */
    void connected(Channel channel) throws RemotingException;

    /**
     * on channel disconnected.
     * 
     * @param channel channel.
     */
    void disconnected(Channel channel) throws RemotingException;

    /**
     * on message sent.
     * 
     * @param channel channel.
     * @param message message.
     */
    void sent(Channel channel, Object message) throws RemotingException;

    /**
     * on message received.
     * 
     * @param channel channel.
     * @param message message.
     */
    void received(Channel channel, Object message) throws RemotingException;

    /**
     * on exception caught.
     * 
     * @param channel channel.
     * @param exception exception.
     */
    void caught(Channel channel, Throwable exception) throws RemotingException;
}

ChannelHandler主要是定义跟“管道“相关的接口,这些操作是在netty收到某类远程传来的消息时候触发的行为,是一个比较底层的接口类,之后我们凡是看到Handler结尾的类,大多数都实现了这个接口。

public interface ExchangeHandler extends ChannelHandler, TelnetHandler {

    /**
     * reply.
     * 
     * @param channel
     * @param request
     * @return response
     * @throws RemotingException
     */
    Object reply(ExchangeChannel channel, Object request) throws RemotingException;

}

ExchangeHandler仅仅是作为Handler的扩展子类,其就多一个方法。但是这个方法恰恰表情ExchangeHandler的作用范围:Exchange层面主要是用于信息交换的层面,用于同步转异步操作,其操作的核心就是Request和Response。上面的reply方法就是将request转换为Response的核心接口方法,定义在ExchangeHandler中也十分清晰。

然后经过上面的介绍之后我们再看一下requestHandler的具体内容:

//这个类是装饰者模式下最底层的ChannelHandler类
    private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
        
        public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
            //如果Message是调用请求的话
            if (message instanceof Invocation) {
                Invocation inv = (Invocation) message;
                //根据exporter领取相关联的invoker,通过inv参数构建出需要的三元素,然后找到对应的exporter,再通过exporter找到对应的invoker
                Invoker<?> invoker = getInvoker(channel, inv);
            
                RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
                //最终还是委托到invoker去调用真实的接口
                return invoker.invoke(inv);
            }
            throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
        }
        // 因为ExchangeHandlerAdapter也是ChannelHandler的接口实现类,所以需要实现相关接口
        @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);
        }

        @Override
        public void disconnected(Channel channel) throws RemotingException {
            if(logger.isInfoEnabled()){
                logger.info("disconected from "+ channel.getRemoteAddress() + ",url:" + channel.getUrl());
            }
            invoke(channel, Constants.ON_DISCONNECT_KEY);
        }
        
        //根据三要素构造出来对应的Invocation,然后调用其方法,这里主要处理的是配置的方法,
        private void invoke(Channel channel, String methodKey) {
            Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
            if (invocation != null) {
                try {
                    received(channel, invocation);
                } catch (Throwable t) {
                    logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
                }
            }
        }
        
        private Invocation createInvocation(Channel channel, URL url, String methodKey) {
            String method = url.getParameter(methodKey);
            if (method == null || method.length() == 0) {
                return null;
            }
            RpcInvocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);
            invocation.setAttachment(Constants.PATH_KEY, url.getPath());
            invocation.setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));
            invocation.setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));
            invocation.setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY));
            if (url.getParameter(Constants.STUB_EVENT_KEY, false)){
                invocation.setAttachment(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString());
            }
            return invocation;
        }
    };

看完了requestHandler,我们再来继续看看那server = Exchangers.bind(url, requestHandler);
继续跟踪方法看到其实调用的是:
server = new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
这里的装饰者模式可以看一下:

==1.== handler,也就是上面提到的requestHandler

==2.== HeaderExchangeHandler 包装了handler

// 个人理解:Exchange主要是处理Request和Response的逻辑,所以这里就是调动底层的方法将Request的处理结果转换为Response
    Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
        //构造Request对应的Response
        Response res = new Response(req.getId(), req.getVersion());
        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);
            res.setStatus(Response.BAD_REQUEST);

            return res;
        }
        // 根据massage类型找到对应的Handler
        Object msg = req.getData();
        try {
            // 执行与Exporter交接的最初的Handler
            Object result = handler.reply(channel, msg);
            res.setStatus(Response.OK);
            res.setResult(result);
        } catch (Throwable e) {
            res.setStatus(Response.SERVICE_ERROR);
            res.setErrorMessage(StringUtils.toString(e));
        }
        return res;
    }

这一层主要是借助与requestHandler的返回结果,将其封装成具体的Response然后返回。

==3.== DecodeHandler 包装了HeaderExchangeHandler
DecodeHandler主要是复写了received方法,将收到的请求信息解码然后传入下一层的Handler调用。因为编码解码的整体不打算在本章涉及,所以这里先忽略。

通过上面的三部,我们看到了这里对外暴露的是DecodeHandler,然后我们继续跟着原来的逻辑看:

server = new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
进一步跟踪代码可以得到下面的逻辑:
server = new NettyServer(url, listener),这里的listener其实就是刚才我们装饰者模式下生成的DecodeHandler。然后我们进入到NettyServer看一下。

public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
        //在URL山添加对应的线程名称然后包装handler
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }

这里又是装饰者模式,而且又对刚才的DecodeHandler进行包装,我们看一下包装的逻辑:

public static ChannelHandler wrap(ChannelHandler handler, URL url){
        return ChannelHandlers.getInstance().wrapInternal(handler, url);
    }

    protected ChannelHandlers() {}

    
    protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {
        return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
                                        .getAdaptiveExtension().dispatch(handler, url)));
    }

从上面的代码中我们可以看到这里又对handler做了三层包装。我们接着上面讲过的三次包装继续看这写包装:

==4.== ExtensionLoader.getExtensionLoader(Dispatcher.class).getAdaptiveExtension()获得的是默认的AllDispatcher,所以这里的第四层包装就是AllChannelHandler。

    public AllChannelHandler(ChannelHandler handler, URL url) {
        super(handler, url);
    }
    父类构造函数如下:
    public WrappedChannelHandler(ChannelHandler handler, URL url) {
        this.handler = handler;
        this.url = url;
        // 因为我司们日常使用的都是fixThreadPool,所以这里就以FixedThreadPool来一看具体获取到的线程池:
        executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
        
        //感觉就是区分consumer和provider端,这里看意义不大
        String componentKey = Constants.EXECUTOR_SERVICE_COMPONENT_KEY;
        if (Constants.CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(Constants.SIDE_KEY))) {
            componentKey = Constants.CONSUMER_SIDE;
        }
        DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
        dataStore.put(componentKey, Integer.toString(url.getPort()), executor);
    }
    
    // 根据url中取到的参数,定义一个线程池,这个线程池非常重要,在我们的工程中执行任务的线程就是这个线程池中的线程,
    // 在我们的dubbo配置中,threads和queues都是重要的参数
    public Executor getExecutor(URL url) {
        String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
        int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
        int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
        return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS, 
                queues == 0 ? new SynchronousQueue<Runnable>() : 
                    (queues < 0 ? new LinkedBlockingQueue<Runnable>() 
                            : new LinkedBlockingQueue<Runnable>(queues)),
                new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
    }

==5.== HeartbeatHandler 包装了AllChannelHandler

这个Handler的操作顾名思义,就是处理心跳有关的操作,我们简单看一下:
    public void connected(Channel channel) throws RemotingException {
        //在channel上设置对应的读写时间
        setReadTimestamp(channel);
        setWriteTimestamp(channel);
        handler.connected(channel);
    }
    该Handler中比较重要的操作是:
    public void received(Channel channel, Object message) throws RemotingException {
        setReadTimestamp(channel);
        //如果是心跳请求或者心跳响应的话就会直接在此步骤进行处理,不会在继续调用之后的handler,减少了不必要的调用。
        if (isHeartbeatRequest(message)) {
            Request req = (Request) message;
            if (req.isTwoWay()) {
                Response res = new Response(req.getId(), req.getVersion());
                res.setEvent(Response.HEARTBEAT_EVENT);
                channel.send(res);
                if (logger.isInfoEnabled()) {
                    int heartbeat = channel.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
                    if(logger.isDebugEnabled()) {
                        logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress()
                                        + ", cause: The channel has no data-transmission exceeds a heartbeat period"
                                        + (heartbeat > 0 ? ": " + heartbeat + "ms" : ""));
                    }
                }
            }
            return;
        }
        if (isHeartbeatResponse(message)) {
            if (logger.isDebugEnabled()) {
                logger.debug(
                    new StringBuilder(32)
                        .append("Receive heartbeat response in thread ")
                        .append(Thread.currentThread().getName())
                        .toString());
            }
            return;
        }
        handler.received(channel, message);
    }
    综上来看,有了这一层的好处就是某些请求可以在这里直接处理掉,不用再往之后的handler中传递。

==6.== MultiMessageHandler包装了HeartbeatHandler

    // 这个是MultiMessageHandler中的核心方法
    @SuppressWarnings("unchecked")
    @Override
    public void received(Channel channel, Object message) throws RemotingException {
        // 在所有的Handler的最开端处理,如果请求信息是MultiMessage的话,代表可能是多个请求合并而成的请求,所以遍历处理
        if (message instanceof MultiMessage) {
            MultiMessage list = (MultiMessage)message;
            for(Object obj : list) {
                handler.received(channel, obj);
            }
        } else {
            handler.received(channel, message);
        }
    }

到这里基本的Handler包装已经差不多完了,然后我们就接着原来的逻辑继续看:

    public NettyServer(URL url, ChannelHandler handler) throws RemotingException{<br>
        //在URL山添加对应的线程名称然后包装handler
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }
    
    public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);
        //获取服务暴露所需参数,host,port
        localAddress = getUrl().toInetSocketAddress();
        String host = url.getParameter(Constants.ANYHOST_KEY, false) 
                        || NetUtils.isInvalidLocalHost(getUrl().getHost()) 
                        ? NetUtils.ANYHOST : getUrl().getHost();
        bindAddress = new InetSocketAddress(host, getUrl().getPort());
        this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
        this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
        try {
            //具体的open操作落到的NettyServer的实现上
            doOpen();
            if (logger.isInfoEnabled()) {
                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
            }
        } catch (Throwable t) {
            throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName() 
                                        + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
        }
        if (handler instanceof WrappedChannelHandler ){
            executor = ((WrappedChannelHandler)handler).getExecutor();
        }
    }
    
    @Override
    protected void doOpen() throws Throwable {
        NettyHelper.setNettyLoggerFactory();
        //设置线程池(但是线程池中的线程都是守护线程,为的就是当JVM退出时候不用考虑守护线程是否已经结束)
        ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
        ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
        ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
        bootstrap = new ServerBootstrap(channelFactory); //Netty启动类
        //定义NettyHandler(这个应该是通用的Handler,只有在服务启动的时候生效一次)
        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
        channels = nettyHandler.getChannels();
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            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());
    }
    

这里的逻辑就是一般的Netty初始化逻辑,并没有做任何特殊处理的地方。唯一需要注意的就是nettyHandler,nettyHandler中包含了对于当前nettyServer的引用,所以最终的事件处理还是通过上面层层包装的Handler来处理。

到这里,整体上的服务暴露环节已经讲完。本节的重点就是理解每一层的Handler都干了什么事情,其实这里涉及很多细节的内容都没有讲到,例如参数的含义,Stub,回调的处理等等。因为这次是第一次读源码,所以重点在于对于核心流程的理解,先理解核心概念,后面再在核心流程里面补充其他的细节。

上一篇下一篇

猜你喜欢

热点阅读