深度解析dubbo服务远程暴露(二)

注:本文基于dubbo2.6.x

1. protocol.export(invoker)

接着《深度解析dubbo服务远程暴露(一)》这篇的讲,我们讲到exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);/// dubbo protocol,我们来看一下RegistryProtocol类的private Protocol protocol 成员,这个成员的值是由dubbo spi 扩展技术在实例完成后setter注入进来的,实际上这里protocol注入的是ExtensionLoader.getExtensionLoader(Protocol).getAdaptiveExtension();是自适应的实现类。我们可以看下自适应的实现类的export方法:


    public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }

然后invokerDelegete 这个委托类getUrl得到的是protocol=dubbo的url,所以

Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");

最后获取的extension是经过dubbo spi setter注入与wrapper包装后的。
在这里插入图片描述
Listener-----> Filter----->Qos-----> Dubbo
这里的包装其实是没有顺序的,因为在包装的时候是遍历的一个ConcurrentHashSet集合,可以看下包装这块代码
在这里插入图片描述
所以我们每次启动可能会看到不一样的包装顺序。
我们先来看下ProtocolListenerWrapper这个类

2.ProtocolListenerWrapper

  @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {// registry   是否是注册中心
            return protocol.export(invoker);
        }
        return new ListenerExporterWrapper<T>(protocol.export(invoker),// dubbo export  dubbo --->暴露服务 生成exporter
                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                        .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));//  exporter listener
    }

这里不是REGISTRY_PROTOCOL,然后走了下面使用ListenerExporterWrapper 将服务暴露返回的exporter与自动激活的listener们绑在了一起。我们可以看下这个ListenerExporterWrapper类,

public class ListenerExporterWrapper<T> implements Exporter<T> {
    private static final Logger logger = LoggerFactory.getLogger(ListenerExporterWrapper.class);
    private final Exporter<T> exporter;
    private final List<ExporterListener> listeners;
    public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) {
        if (exporter == null) {// 判断null
            throw new IllegalArgumentException("exporter == null");
        }
        this.exporter = exporter;
        this.listeners = listeners;
        if (listeners != null && !listeners.isEmpty()) {
            RuntimeException exception = null;
            for (ExporterListener listener : listeners) {//遍历通知
                if (listener != null) {
                    try {
                        listener.exported(this);//通知
                    } catch (RuntimeException t) {
                        logger.error(t.getMessage(), t);
                        exception = t;
                    }
                }
            }
            if (exception != null) {
                throw exception;
            }
        }
    }
   ...
}

我们可以看到就是遍历通知listener,告诉他们当前服务暴露完了。这些listener们我们后面单独拿出来解析。

3.ProtocolFilterWrapper

接下来我们看下ProtocolFilterWrapper类的export方法

@Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {/// registry protocol  就到下一个wapper 包装对象中就可以了
            return protocol.export(invoker);
        }
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

这里不是registry protocol 然后会走下面,我们看下buildInvokerChain 方法,这里buildInvokerChain的三个参数分别是
invoker, service.filter,provider

 private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;  
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (!filters.isEmpty()) {
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {
                    ...
                    //exception->moniter->timeout->trace->context->generic->classloader->echo
                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }
                   ...
                };
            }
        }
        return last;
    }

这个方法实际上是根据dubbo spi 扩展技术自动激活的特性获取到对应的filter们,然后一层一层的包装这个invoker,生成一个过滤调用链,最后到真实的invoker上面。这个Filter我们后期会单独拿出来解析,这里只需要知道它一层层包装了invoker就行。

4.QosProtocolWrapper

 public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        //判断是registry
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            // 启动Qos服务器?
            startQosServer(invoker.getUrl());
            return protocol.export(invoker);
        }
        return protocol.export(invoker);
    }

这里同理不是registry protocol ,直接到了下一层

5. DubboProtocol

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

        // export service.  key = com.alibaba.dubbo.demo.DemoService:20880
        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;
    }

这里首先是根据url生成一个服务的key,创建一个DubboExporter 把invoker,key与缓存exporter的map 绑在一起。将 创建的这个exporter缓存到exporterMap里面。我们可以exporterMap的定义:

protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();

其中key就是生成的那个serviceKey,然后value是创建的那个exporter。
接着就是调用openServer(url),来打开服务器,我们看下源码:

 // 打开server
    private void openServer(URL url) {
        // find server.    获得地址 ip:port   192.168.1.104:20880
        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) {// 没有找到server 就要创建server
                serverMap.put(key, createServer(url));
            } else {
                // server supports reset, use together with override  server 支持重置,与覆盖一起使用
                server.reset(url);   //
            }
        }
    }

首先是获取地址,这个地址形式是ip:port,获取isserver参数,默认是true,然后从serverMap这个缓存中查找对应的server,如果之前没有创建过,就调用createServer(url) 来创建server,之后把创建的server缓存在serverMap中,我们先看下serverMap这个成员变量

private final Map<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>(); // <host:port,Exchanger>

其中key是服务器地址,也就是上面url.getAddress();获得的,value就是对应的ExchangeServer。
我们再来看看是怎么创建server的,看下createServer(url)这个方法的源码:

// 创建server
    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    60 * 1000   设置心跳
        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
        //获取配置的服务器类型, 缺省就是使用netty 默认服务器是netty
        String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);  // 获取的使用的server  缺省使用netty
        // 不存在Transports 话  , 就抛出 不支持的server type
        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
            throw new RpcException("Unsupported server type: " + str + ", url: " + url);
        // 设置  Codec 的类型为dubbo
        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);// client
        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;
    }

这个方法前面部分就是设置一些参数,channel.readonly.sent =true,就是服务器关闭的时候指发送只读属性,heartbeat=60*1000 设置默认的心跳时间,获取server,缺省的情况下使用netty,设置Codec 的类型为dubbo。
然后Exchangers.bind(url, requestHandler),其实这个Exchangers 是个门面类,封装了bind与connect两个方法的调用

6.Exchanger

我们可以看看Exchanger的方法们
在这里插入图片描述
我们接着,看下当前调用的bind方法:

// bind
    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"); // 如果没有codec  的话 就设置为exchange
        // exchanger.bind()
        return getExchanger(url).bind(url, handler);
    }

前面的都是参数校验,我们看下getExchanger(url) 方法:

 public static Exchanger getExchanger(URL url) {
        // 获取exchanger  缺省header
        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);
}

这里可以看到从url中获取exchanger ,缺省是header,然后使用dubbo spi 获取到HeaderExchanger,我们看下HeaderExchanger源码:

7.HeaderExchanger

public class HeaderExchanger implements Exchanger {
    public static final String NAME = "header";
    @Override
    public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {

        // 创建一个通信client
        //DecodeHandler => HeaderExchangeHandler => ExchangeHandler( handler ) 。
        return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
    }
    @Override
    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {

        // 创建一个通信server  DecodeHandler  << HeaderExchangeHandler  << handler
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }
}

这里有三个点,一是将这handler又包装了两层,二是使用Transporters 这个门面类进行bind,再就是创建HeaderExchangeServer 将server进行增强,其实HeaderExchangeServer 这个是专门发送心跳的。
我们先看下Transporters这个类

8.Transporters

这个Transporters也是门面类,对外统一了bind 与connect。我们只看下与这次有关的部分

 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 {   // 多个channal  对 Channel分发   ChannelHandlerDispatcher 循环
            handler = new ChannelHandlerDispatcher(handlers);
        }
        // 真正服务器 进行bind
        return getTransporter().bind(url, handler);
    }
public static Transporter getTransporter() {  // 获取transporter
        return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}

首先是参数校验,接着判断handler的个数,如果一个话还好说,多个话就要使用ChannelHandlerDispatcher类来包装了,其实里面就是对多个handler循环调用,接着调用getTransporter获取Transporter扩展点的自适应类。
我们可以稍微看下Transporter 这个扩展点的代码,

@SPI("netty") // 默认是netty3的
public interface Transporter {

    /**
     * Bind a server.
     *
     * @param url     server url
     * @param handler
     * @return server
     * @throws RemotingException
     * @see com.alibaba.dubbo.remoting.Transporters#bind(URL, ChannelHandler...)
     */
    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;

    /**
     * Connect to a server.
     *
     * @param url     server url
     * @param handler
     * @return client
     * @throws RemotingException
     * @see com.alibaba.dubbo.remoting.Transporters#connect(URL, ChannelHandler...)
     */
    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;

}

可以看到bind自适应实现类是根据server 与transporter 这两个参数决定的,server这个参数咱们上面的DubboProtocol类里面createServer 方法中就设置好了,缺省使用netty,我们通过看dubbo spi 配置文件
在这里插入图片描述
netty对应的也就是NettyTransporter这个实现类。

9.NettyTransporter

/**
 * 实现 Transporter 接口,基于 Netty4 的网络传输实现类
 */
public class NettyTransporter implements Transporter {
    /**
     * 扩展名
     */
    public static final String NAME = "netty";

    @Override
    public Server 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);
    }

}

我们可以看到new了一个NettyServer对象。我们来看下代码

10.NettyServer

在看NettyServer前先看下它的继承结构
在这里插入图片描述
看下NettyServer的构造方法:

   public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }

调用的父类的构造
先看下AbstractServer代码

public abstract class AbstractServer extends AbstractEndpoint implements Server {
    protected static final String SERVER_THREAD_POOL_NAME = "DubboServerHandler";
    private static final Logger logger = LoggerFactory.getLogger(AbstractServer.class);
    // 线程池
    ExecutorService executor;
    // 服务地址
    private InetSocketAddress localAddress;
    //绑定地址
    private InetSocketAddress bindAddress;
    // 服务器最大可接受连接数
    private int accepts;
    // 空闲超时时间
    private int idleTimeout = 600;
    public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);
        localAddress = getUrl().toInetSocketAddress();
        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());//ip
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort()); //port
        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()));
    }

    protected abstract void doOpen() throws Throwable;
}

我们可以看到AbstractServer的构造前面都是些参数的获取,之后调用doOpen()方法,具体是由子类实现的我们在看看NettyServer的doOpen方法

   @Override
    protected void doOpen() throws Throwable {
        bootstrap = new ServerBootstrap();
        bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
        //iothreads  默认是cpu核心数+1  与 32 进行比较,取小的那个  也就是最大不超过32
        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();
/**
 *  ChannelOption.SO_REUSEADDR  这个参数表示允许重复使用本地地址和端口,
 *
 *     比如,某个服务器进程占用了TCP的80端口进行监听,此时再次监听该端口就会返回错误,使用该参数就可以解决问题,该参数允许共用该端口,这个在服务器程序中比较常使用,
 *
 *     比如某个进程非正常退出,该程序占用的端口可能要被占用一段时间才能允许其他进程使用,而且程序死掉以后,内核一需要一定的时间才能够释放此端口,不设置SO_REUSEADDR
 *
 *     就无法正常使用该端口。
 */
        // 设置线程组
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)// 设置channel类型  nio
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)//禁止使用nagle算法
                .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服务器,然后boss线程是一个,然后work线程是是cpu数+1然后与32做比较,取最小的那个,也就是最大不超过32个线程。
到这里我们的服务就算启动完成了。
我们再回过头来看看HeaderExchangeServer这个维护心跳的

11. HeaderExchangeServer

 public HeaderExchangeServer(Server server) {
        if (server == null) {
            throw new IllegalArgumentException("server == null");
        }

        // 服务器
        this.server = server;

        //心跳
        this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);

        // 心跳超时
        this.heartbeatTimeout = server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
        if (heartbeatTimeout < heartbeat * 2) {
            throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
        }

        // 启动心跳
        startHeartbeatTimer();
    }

这里有heartbeat 与heartbeatTimeout 这两个参数,我们可以看到heartbeat 缺省是0 ,但是我们在DubboProtocol的createServer创建server方法设置了值为60 * 1000,也就是60s,然后heartbeatTimeout就是3*60s
接着我们看下启动心跳timer的方法startHeartbeatTimer;

 //启动心跳timer
    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);
        }
    }

好了,到这里我们远程服务暴露中启动部分就算结束了,这里我们并没有说太多的细节,我只需要把服务暴露服务器启动这个过程缕顺就可以了,具体的细节方面放到后面分析。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

$码出未来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值