Dubbo源码学习07

RegistryProtocol.export服务导出流程:导出服务ExporterChangeableWrapper->注册服务到注册中心->订阅注册中心overrideSubscribeUrl数据;篇幅有限,本篇幅主要分析导出服务ExporterChangeableWrapper源码实现

RegistryProtocol.export(final Invoker<T> originInvoker)

@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //export invoker
        //导出服务
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
        // 获取注册中心 URL,以 zookeeper 注册中心为例,得到的示例 URL 如下:
        // zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F172.17.48.52%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider
        URL registryUrl = getRegistryUrl(originInvoker);

        //registry provider
        // 根据 URL 加载 Registry 实现类,比如 ZookeeperRegistry
        final Registry registry = getRegistry(originInvoker);
        // 获取已注册的服务提供者 URL,比如:
        // dubbo://172.17.48.52:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello
        final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker);

        //to judge to delay publish whether or not
        //获取register参数;register表示是否注册到注册中心
        boolean register = registeredProviderUrl.getParameter("register", true);
        //缓存到ProviderConsumerRegTable的表中
        ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);
        //注册服务到zookeeper
        if (register) {
            register(registryUrl, registeredProviderUrl);
            //找到该originInvoker对应的ProviderInvokerWrapper设置reg属性为true
            ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
        }

        // Subscribe the override data
        // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
        // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
        //创建监听器
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        //放入overrideSubscribeUrl对应的OverrideListener
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
        // 向注册中心进行订阅 override 数据
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
        //Ensure that a new exporter instance is returned every time export
        //创建并返回DestroyableExporter
        return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registeredProviderUrl);
    }

该方法核心代码可以简化为

@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //1.导出invoker;
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
        final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker);
        ....
        boolean register = registeredProviderUrl.getParameter("register", true);
        //2.注册服务到zookeeper
        if (register) {
            register(registryUrl, registeredProviderUrl);
        }
        ....
        //3.向注册中心进行订阅overrideSubscribeUrl
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
        return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registeredProviderUrl);
    }

doLocalExport(final Invoker<T> originInvoker)

private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
        //获取originInvoker对应的缓存key
        String key = getCacheKey(originInvoker);
        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
        if (exporter == null) {
            synchronized (bounds) {
                exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
                if (exporter == null) {
                    //创建invoker委托类对象
                    final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
                    //调用protocol的export方法导出服务
                    //这个时候返回的url肯定是类似dubbo://....这样子的所以protocol
                    //由于dubbo的spi机制,此时肯定是通过DubboProtocol的export方法
                    exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
                    bounds.put(key, exporter);
                }
            }
        }
        return exporter;
    }

通过originInvoker获取缓存key,然后就是dubbo惯用的缓存伎俩,从缓存中获取,如果没有成功获取到,调用成员变量protocol的export方法导出invoker,这里的protocol还是Protocol$Adaptive,所以又会经过https://blog.csdn.net/qq_23536449/article/details/102639888文中描述的

QosProtocolWrapper,ProtocolFilterWrapper,ProtocolListenerWrapper,通过debug代码可以清楚看到执行流程:

  • QosProtocolWrapper

  • ProtocolFilterWrapper

  • ProtocolListenerWrapper

  • 最后会进入到DubboProtocol的export方法中,下文会分析

getCacheKey(originInvoker)

private String getCacheKey(final Invoker<?> originInvoker) {
        URL providerUrl = getProviderUrl(originInvoker);
        String key = providerUrl.removeParameters("dynamic", "enabled").toFullString();
        return key;
    }

/**
     * Get the address of the providerUrl through the url of the invoker
     *
     * 通过调用者的地址获取providerUrl的地址
     * @param origininvoker
     * @return
     */
    private URL getProviderUrl(final Invoker<?> origininvoker) {
        String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY);
        if (export == null || export.length() == 0) {
            throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl());
        }

        URL providerUrl = URL.valueOf(export);
        return providerUrl;
    }
  • key类似:dubbo://ip:port/com.alibaba.dubbo.study.day01.xml.service.EchoService?addListener.1.callback=true&addListener.retries=2&anyhost=true&application=echo-provider&bean.name=com.alibaba.dubbo.study.day01.xml.service.EchoService&bind.ip=169.254.22.149&bind.port=20880&dubbo=2.0.2&generic=false&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=3600&side=provider&timestamp=1572674213180

DubboProtocol.export(Invoker<T> invoker)

 @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        URL url = invoker.getUrl();

        // export service.
        // 获取服务标识,理解成服务坐标也行。由服务组名,服务名,服务版本号以及端口组成。比如:
        // demoGroup/com.alibaba.dubbo.demo.DemoService:1.0.1:20880
        String key = serviceKey(url);
        // 创建dubbo的exporter
        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;
    }

构造DubboExporter,放入缓存;存根服务处理(这里可以略过);启动服务器;优化序列化(非重点关心)

openServer(url);

private void openServer(URL url) {
        // 获取 host:port,并将其作为服务器实例的 key,用于标识当前的服务器实例
        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) {
            //获取address对应的server,
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
                //创建服务器实例并放入缓存中
                serverMap.put(key, createServer(url));
            } else {
                // server supports reset, use together with override
                // 服务器已创建,则根据 url 中的配置重置服务器
                server.reset(url);
            }
        }
    }

如上,在同一台机器上(单网卡),同一个端口上仅允许启动一个服务器实例。若某个端口上已有服务器实例,此时则调用 reset 方法重置服务器的一些配置,让我们先来分析下创建服务器实例方法。

createServer(URL url)

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 = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
        // 获取server参数
        String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
        // 通过 SPI 检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常
        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 {
            //创建ExchangeServer
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }
        /**
         * 获取client代表的Transporter是否存在,如果不存在抛出异常好了
         */
        str = url.getParameter(Constants.CLIENT_KEY);
        if (str != null && str.length() > 0) {
            // 获取所有的 Transporter 实现类名称集合,比如 supportedTypes = [netty, mina]
            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
            // 检测当前 Dubbo 所支持的 Transporter 实现类名称列表中,
            // 是否包含 client 所表示的 Transporter,若不包含,则抛出异常
            if (!supportedTypes.contains(str)) {
                throw new RpcException("Unsupported client type: " + str);
            }
        }
        return server;
    }

添加默认参数,然后检测指定的server类型(netty,minal,gizzly)的Transproter是否存在,使用Exchangers创建的bind方法创建server实例,检测是否支持 client 参数所表示的 Transporter 拓展,不存在也是抛出异常。

Exchangers.bind(url, requestHandler)

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) {
        //获取url中的exchanger属性,没有使用默认的header
        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
        return getExchanger(type);
    }
public static Exchanger getExchanger(String type) {
        //dubbo的spi机制
        return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
    }

通过Exchanger命名理解设计他的初衷,交换层,输入url和一个handler,输出server(比如类型为HeaderExchangeServer),然后dubbo本身支持spi扩展,所以exchanger也支持扩展(dubbo默认实现了一个扩展名称为header的Exchager即下文的HeaderExchanger)。

HeaderExchanger.bind(URL url, ExchangeHandler handler)

@Override
    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

调用Transporters.bind方法创建Server(包装handler为DecodeHandler),类型可以为Netty、Mina、Grizzly,使用HeaderExchageServer装饰创建出来的Server,HeaderExchangeServer为Server提供了发送空闲channel检测、channel重连的功能

Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))

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 static Transporter getTransporter() {
        return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
    }

通过spi获取获取不同类型的Transporter,默认是NettyTransporter,至于为什么请看Transporter$Adaptive的bind方法

package com.alibaba.dubbo.remoting;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Transporter$Adaptive implements com.alibaba.dubbo.remoting.Transporter {
    public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg0;
        String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
        com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
        return extension.connect(arg0, arg1);
    }

    public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
        if (arg0 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg0;
        String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
        com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
        return extension.bind(arg0, arg1);
    }
}

NettyTransporter.bind(URL url, ChannelHandler listener)

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

创建一个Netty类型的服务实例!!!NettyServer的类继承关系图如下

  • ChannelHandler:主要是处理channel的,比如channel关闭、连接,发送消息,接收消息等操作
  • Resetable:该接口实现类可以实现例如Server相关属性的重置
  • AbstractPeer:主要是保存了服务提供这协议的url和通过委托成员变量ChannelHandler实现了ChannelHandler接口的方法和EndPoint接口的方法
/**
     * channel事件处理器
     */
    private final ChannelHandler handler;
    /**
     * 第一个服务提供者协议的url地址
     */
    private volatile URL url;
  • AbstractEndPoint:实现了Resetable接口reset方法,使得我们可以重置编码器、超时时间、连接超时时间
/**
     * 编码解码器
     */
    private Codec2 codec;
    /**
     *  超时时间
     */
    private int timeout;
    /**
     * 连接超时时间
     */
    private int connectTimeout;
  • AbstractServer:该类实现了Server接口,作为Mina、Netty、Grizzly类型的服务端统一实现,并且该类重写了AbstractEndPoint的reset方法
 ExecutorService executor;
    /**
     * url host:port地址。
     */
    private InetSocketAddress localAddress;
    /**
     * 如果是多网卡,并且指定了 bind.ip、bind.port,如果为空,与localAddress相同。
     */
    private InetSocketAddress bindAddress;
    /**
     *  AbstractServer#accepts未使用到。
     */
    private int accepts;
    /**
     * 空闲时间
     */
    private int idleTimeout = 600; //600 seconds
  • NettyServer:服务端实现类
/**
     * < ip:port, channel> 所有通道。
     */
    private Map<String, Channel> channels; // <ip:port, channel>
    /**
     * netty 服务端启动器。
     */
    private ServerBootstrap bootstrap;
    /**
     * 服务端监听通道。
     */
    private io.netty.channel.Channel channel;
    /**
     * Netty boss线程组(负责连接事件)
     */
    private EventLoopGroup bossGroup;
    /**
     *  work线程组(负责IO事件)
     */
    private EventLoopGroup workerGroup;
  • 回顾下上在DubboProtocol.openServer(URL url)代码中的如下代码片段可知其真正的实现在AbstractServer中。

AbstractServer.reset(URL url)

@Override
    public void reset(URL url) {
        if (url == null) {
            return;
        }
        try {
            //url中是否包含accepts属性,存在并且大于0则重置Server的accepts
            if (url.hasParameter(Constants.ACCEPTS_KEY)) {
                int a = url.getParameter(Constants.ACCEPTS_KEY, 0);
                if (a > 0) {
                    this.accepts = a;
                }
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
        try {
            //url中是否包含idle.timeout属性,存在并且大于0则重置Server的idle.timeout
            if (url.hasParameter(Constants.IDLE_TIMEOUT_KEY)) {
                int t = url.getParameter(Constants.IDLE_TIMEOUT_KEY, 0);
                if (t > 0) {
                    this.idleTimeout = t;
                }
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
        try {
            //url是否包含threads属性,并且executor是ThreadPoolExecutor的实例并且线程池未被关闭
            if (url.hasParameter(Constants.THREADS_KEY)
                    && executor instanceof ThreadPoolExecutor && !executor.isShutdown()) {
                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                //url中的threads数量
                int threads = url.getParameter(Constants.THREADS_KEY, 0);
                //线程池最大容量
                int max = threadPoolExecutor.getMaximumPoolSize();
                //线程池核心线程数
                int core = threadPoolExecutor.getCorePoolSize();
                if (threads > 0 && (threads != max || threads != core)) {
                    if (threads < core) {
                        threadPoolExecutor.setCorePoolSize(threads);
                        if (core == max) {
                            threadPoolExecutor.setMaximumPoolSize(threads);
                        }
                    } else {
                        threadPoolExecutor.setMaximumPoolSize(threads);
                        if (core == max) {
                            threadPoolExecutor.setCorePoolSize(threads);
                        }
                    }
                }
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
        //合并url中的参数并设置给AbstractPeer的url属性
        super.setUrl(getUrl().addParameters(url.getParameters()));
    }

/**
     * Add parameters to a new url.
     *
     * @param parameters parameters in key-value pairs
     * @return A new URL
     */
    public URL addParameters(Map<String, String> parameters) {
        if (parameters == null || parameters.size() == 0) {
            return this;
        }
        //判断当前URL实例的parameter是否与参数中的parameters
        //是否相同,包括键和值,不全相同返回false
        boolean hasAndEqual = true;
        for (Map.Entry<String, String> entry : parameters.entrySet()) {
            String value = getParameters().get(entry.getKey());
            if (value == null) {
                if (entry.getValue() != null) {
                    hasAndEqual = false;
                    break;
                }
            } else {
                if (!value.equals(entry.getValue())) {
                    hasAndEqual = false;
                    break;
                }
            }
        }
        // return immediately if there's no change
        //没有变化直接返回当前url
        if (hasAndEqual) return this;
        //如果有变化合并并且返回新的map
        Map<String, String> map = new HashMap<String, String>(getParameters());
        map.putAll(parameters);
        return new URL(protocol, username, password, host, port, path, map);
    }

覆盖NettyServer的accepts、idleTimeout、threadPoolExecutor的配置;通过调用AbstractPeer的setUrl方法设置新的Url成员变量的值。

HeaderExchangeServer.reset(URL url)

@Override
    public void reset(URL url) {
        server.reset(url);
        try {
            //url中是否包含heartbeat或者heartbeat.timeout属性
            if (url.hasParameter(Constants.HEARTBEAT_KEY)
                    || url.hasParameter(Constants.HEARTBEAT_TIMEOUT_KEY)) {
                int h = url.getParameter(Constants.HEARTBEAT_KEY, heartbeat);
                int t = url.getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, h * 3);
                if (t < h * 2) {
                    throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
                }
                if (h != heartbeat || t != heartbeatTimeout) {
                    heartbeat = h;
                    heartbeatTimeout = t;
                    startHeartbeatTimer();
                }
            }
        } catch (Throwable t) {
            logger.error(t.getMessage(), t);
        }
    }
//发送心跳
private void startHeartbeatTimer() {
        stopHeartbeatTimer();
        if (heartbeat > 0) {
            heartbeatTimer = scheduled.scheduleWithFixedDelay(
                    new HeartBeatTask(new HeartBeatTask.ChannelProvider() {
                        @Override
                        public Collection<Channel> getChannels() {
                            return Collections.unmodifiableCollection(
                                    HeaderExchangeServer.this.getChannels());
                        }
                    }, heartbeat, heartbeatTimeout),
                    heartbeat, heartbeat, TimeUnit.MILLISECONDS);
        }
    }
    //停止心跳那啥
    private void stopHeartbeatTimer() {
        try {
            ScheduledFuture<?> timer = heartbeatTimer;
            if (timer != null && !timer.isCancelled()) {
                timer.cancel(true);
            }
        } catch (Throwable t) {
            logger.warn(t.getMessage(), t);
        } finally {
            heartbeatTimer = null;
        }
    }

可以看到HeaderExchageServer其实是在NettyServer的基础上增加了下面两个功能见HeartBeatTask.java

  • a. 对channel进行 空闲时间检测,超过则关闭连接,节省资源。
  • b. 如果server关闭,则发送消息给client端,不再发送请求到该server。

HeartBeatTask.run

@Override
    public void run() {
        try {
            long now = System.currentTimeMillis();
            for (Channel channel : channelProvider.getChannels()) {
                if (channel.isClosed()) {
                    continue;
                }
                try {
                    //channel上次读数据的时间戳
                    Long lastRead = (Long) channel.getAttribute(
                            HeaderExchangeHandler.KEY_READ_TIMESTAMP);
                    //上次写数据的时间戳
                    Long lastWrite = (Long) channel.getAttribute(
                            HeaderExchangeHandler.KEY_WRITE_TIMESTAMP);
                    //读的时间戳不为null并且空闲读时间大于心跳
                    if ((lastRead != null && now - lastRead > heartbeat)
                            //写的时间戳不为null并且空闲写时间大于心跳
                            || (lastWrite != null && now - lastWrite > heartbeat)) {
                        /**
                         * 发送心跳数据
                         */
                        Request req = new Request();
                        req.setVersion(Version.getProtocolVersion());
                        req.setTwoWay(true);
                        req.setEvent(Request.HEARTBEAT_EVENT);
                        channel.send(req);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Send heartbeat to remote channel " + channel.getRemoteAddress()
                                    + ", cause: The channel has no data-transmission exceeds a heartbeat period: " + heartbeat + "ms");
                        }
                    }
                    //空闲读时间戳不为null并且空闲读大于心跳超时时间
                    if (lastRead != null && now - lastRead > heartbeatTimeout) {
                        logger.warn("Close channel " + channel
                                + ", because heartbeat read idle time out: " + heartbeatTimeout + "ms");
                        //channel是Client则重新连接
                        if (channel instanceof Client) {
                            try {
                                ((Client) channel).reconnect();
                            } catch (Exception e) {
                                //do nothing
                            }
                        //否则关闭channel
                        } else {
                            channel.close();
                        }
                    }
                } catch (Throwable t) {
                    logger.warn("Exception when heartbeat to remote channel " + channel.getRemoteAddress(), t);
                }
            }
        } catch (Throwable t) {
            logger.warn("Unhandled exception when heartbeat, cause: " + t.getMessage(), t);
        }
    }

NettyServer.java

  • 构造函数代码如下:通过super关键字调用父类方法,解析url中的参数给AbstractServer、AbstractPeer、AbstractEndpoint属性赋值,值得注意的是AbstractServer中的doOpen()方法,该方法是个模板方法,子类实现doOpen方法启动服务器
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }

public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);
        localAddress = getUrl().toInetSocketAddress();

        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
        if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
            bindIp = NetUtils.ANYHOST;
        }
        bindAddress = new InetSocketAddress(bindIp, bindPort);
        //设置可接受最大连接数
        this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
        //连接空闲时间
        this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
        try {
            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);
        }
        //fixme replace this with better method
        DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
        executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
    }

ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME))

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

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

Dispatcher通过dubbo的spi机制最终为AllDispatcher,我们来看下Dispatcher$Adaptive的代码就能知晓原因

package com.alibaba.dubbo.remoting;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Dispatcher$Adaptive implements com.alibaba.dubbo.remoting.Dispatcher {
    public com.alibaba.dubbo.remoting.ChannelHandler dispatch(com.alibaba.dubbo.remoting.ChannelHandler arg0, com.alibaba.dubbo.common.URL arg1) {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        String extName = url.getParameter("dispatcher", url.getParameter("dispather", url.getParameter("channel.handler", "all")));
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Dispatcher) name from url(" + url.toString() + ") use keys([dispatcher, dispather, channel.handler])");
        com.alibaba.dubbo.remoting.Dispatcher extension = (com.alibaba.dubbo.remoting.Dispatcher) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Dispatcher.class).getExtension(extName);
        return extension.dispatch(arg0, arg1);
    }
}

AllDispatcher.dispatch(ChannelHandler handler,URL url)

/**
 * default thread pool configure
 */
public class AllDispatcher implements Dispatcher {

    public static final String NAME = "all";

    @Override
    public ChannelHandler dispatch(ChannelHandler handler, URL url) {
        return new AllChannelHandler(handler, url);
    }

}

handler的包装流程如下

DubboProtocol.createServer
    #此时requestHandler为DubboProtocol中的匿名内部类实现ExchangeHandlerAdapter
    ->Exchangers.bind(url,requestHandler) 
        #该方法对handler做了两次包装ExchangeHandlerAdapter->HeaderExchangeHandler->DecodeHandler
        ->HeaderExchanger.bind(url,handler)
            #到这里handler已经为DecodeHandler了
            ->NettyServer(url,handler)
                #该方法首先通过dubbo的spi机制将handler包装为AllChannelHandler->HeartbeatHandler->MultiMessageHandler
                ->ChannelHandlers.wrapInternal(handler,url)

 MultiMessageHandler -> HeartbeatHandler -> AllChannelHandler ->  DecodeHandler ->  HeaderExchangeHandler -> DubboProtocol.requestHandler

  • MultiMessageHandler:检查消息是否为MutiMessage ,如果 是,分开单条调用后续handler
  • HeartbeatHanlder:1.在每个channel动作,对channel标记时间属性, 2. 检查是否心跳请求,是则直接返回心跳,不继续后续请求。
  •  AllChannelHandler : 1. 将后续handler 包装成 ChannelEventRunnable,捕获后续执行的异常,记录日志 。 2. 包装的runnable 放到独立线程池运行, 达到全流程异步化效果。
  • DecodeHandler:判断message的种类然后解码

Netty.doOpen()

@Override
    protected void doOpen() throws Throwable {
        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();

    }

Netty服务端启动的标准模板:主要关注的点就是添加的编码器、解码器、NettyServerHandler。

目前的handler包装链如下:

NettyServerHandler->NettyServer-> MultiMessageHandler -> HeartbeatHandler -> AllChannelHandler ->  DecodeHandler ->  HeaderExchangeHandler -> DubboProtocol.requestHandler

NettyCodecAdapter.java

  • 成员变量如下
//内部编码
private final ChannelHandler encoder = new InternalEncoder();
//内部解码器
private final ChannelHandler decoder = new InternalDecoder();
//AbstractEndpoint中的编解码属性codec怎么获取的参考AbstractEndpoit.getChannelCodec(URL url)
private final Codec2 codec;

 

InternalEncoder.java

private class InternalEncoder extends MessageToByteEncoder {

        @Override
        protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
            com.alibaba.dubbo.remoting.buffer.ChannelBuffer buffer = new NettyBackedChannelBuffer(out);
            Channel ch = ctx.channel();
            NettyChannel channel = NettyChannel.getOrAddChannel(ch, url, handler);
            try {
                codec.encode(channel, buffer, msg);
            } finally {
                NettyChannel.removeChannelIfDisconnected(ch);
            }
        }
    }

委托给编解码器codec处理

InternalDecoder.java

private class InternalDecoder extends ByteToMessageDecoder {

        @Override
        protected void decode(ChannelHandlerContext ctx, ByteBuf input, List<Object> out) throws Exception {

            ChannelBuffer message = new NettyBackedChannelBuffer(input);

            NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);

            Object msg;

            int saveReaderIndex;

            try {
                // decode object.
                do {
                    saveReaderIndex = message.readerIndex();
                    try {
                        msg = codec.decode(channel, message);
                    } catch (IOException e) {
                        throw e;
                    }
                    if (msg == Codec2.DecodeResult.NEED_MORE_INPUT) {
                        message.readerIndex(saveReaderIndex);
                        break;
                    } else {
                        //is it possible to go here ?
                        if (saveReaderIndex == message.readerIndex()) {
                            throw new IOException("Decode without read data.");
                        }
                        if (msg != null) {
                            out.add(msg);
                        }
                    }
                } while (message.readable());
            } finally {
                NettyChannel.removeChannelIfDisconnected(ctx.channel());
            }
        }
    }

委托给编码器codec处理

codec是怎么获取到的?

在doOpen中有以下代码getCodec()

 NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);

getCodec();该方法通过父类AbstractEndpoint继承而来,我们看下codec在AbstractEndpoint的初始化逻辑

protected static Codec2 getChannelCodec(URL url) {
        //获取url中的codec属性默认值为telnet
        String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
        //如果Codec2的SPI实现类包含名称为codecName编解码器的扩展直接加载返回
        if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
            return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
        } else {
            //加载Spi扩展Codec的名称为codecName的编解码器
            return new CodecAdapter(ExtensionLoader.getExtensionLoader(Codec.class)
                    .getExtension(codecName));
        }
    }

未完待续....

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值