dubbo源码解析(7) dubbo服务暴露----远程暴露

56 篇文章 2 订阅

上一篇我们分析了本地暴露,这一篇接着分析远程暴露

定位到ServiceConfig#doExportUrlsFor1Protocol

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

                        Exporter<?> exporter = protocol.export(invoker);
                        exporters.add(exporter);
主要分析这三句话

第一句话和上一篇得本地暴露一样,封装一个invoker对象

protocol依然是一个Protocol$Adaptive,

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

这里的extName=registry

当然,这个extension=ProtocolFilterWrapper,而成员变量为ProtocolListenerWrapper

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}

这里就进入if条件

ProtocolListenerWrapper中的成员变量为RegistryListener

进入RegistryListener#export

final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);进入
exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);

这个protocol依然是Protocol$Adaptive

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

而这句话中的extName=dubbo

这边的extension=ProtocolFilterWrapper,而成员变量为ProtocolListenerWrapper

ProtocolListenerWrapper得成员变量为DubboProtocol

同样看到

ProtocolListenerWrapper中的
return new ListenerExporterWrapper<T>(protocol.export(invoker), 
        Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));

protocol.export(invoker)

进入DubboProtocol#export

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 dispaching 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);
    
    return exporter;
}

首先封装一个 DubboExporter

注入传入得invoker对象已经是含有fitler得了

然后把serviceKey(url);获取到的对象作为key,dubboExpoter作为value丢到

exporterMap中

中间略过

openServer(url);这句话很关键,这里将要打开netty通信,关于netty相关的应用,我会在以后的技术文章里面接着更新
createServer(url))注意到这句话

进入后找到

server = Exchangers.bind(url, requestHandler);这行代码
return getExchanger(url).bind(url, handler);

getExchanger返回的是一个Extension对象

(Exchanger)ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);

这里会返回一个HeaderExchanger,看这个类的bind方法

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

Transporters#bind方法

public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
  。。。。。。

        return getTransporter().bind(url, (ChannelHandler)handler);
   。。。。。
}
public static Transporter getTransporter() {
    return (Transporter)ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}

会返回一个Transporter$Adaptive,拷贝出这个类的bind方法

    public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
        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);
    }
}

extName=netty  所以extension是NettyTransporter。直接进入NettyTransporter的bind方法

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

可以看到这里返回了一个NettyServer的实例

在netty的super类AbstractServer的构造函数中我们看到了

this.doOpen();
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, this.getUrl().getPositiveParameter("iothreads", Constants.DEFAULT_IO_THREADS));
    this.bootstrap = new ServerBootstrap(channelFactory);
    final NettyHandler nettyHandler = new NettyHandler(this.getUrl(), this);
    this.channels = nettyHandler.getChannels();
    this.bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
        public ChannelPipeline getPipeline() {
            NettyCodecAdapter adapter = new NettyCodecAdapter(NettyServer.this.getCodec(), NettyServer.this.getUrl(), NettyServer.this);
            ChannelPipeline pipeline = Channels.pipeline();
            pipeline.addLast("decoder", adapter.getDecoder());
            pipeline.addLast("encoder", adapter.getEncoder());
            pipeline.addLast("handler", nettyHandler);
            return pipeline;
        }
    });
    this.channel = this.bootstrap.bind(this.getBindAddress());
}

这一串就是netty的启动方式,关于netty的应用会在后续文章中提及

回到HeaderExchanger

所以这句代码实际返回的是一个NettyServer

Transporters.bind(url, new ChannelHandler[]{new DecodeHandler(new HeaderExchangeHandler(handler))})

进入HeaderExchangeServer的构造函数

public HeaderExchangeServer(Server server) {
    if (server == null) {
        throw new IllegalArgumentException("server == null");
    } else {
        this.server = server;
        this.heartbeat = server.getUrl().getParameter("heartbeat", 0);
        this.heartbeatTimeout = server.getUrl().getParameter("heartbeat.timeout", this.heartbeat * 3);
        if (this.heartbeatTimeout < this.heartbeat * 2) {
            throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
        } else {
            this.startHeatbeatTimer();
        }
    }
}

这个构造函数中主要是做了心跳检测的定时器

 

回到DubboProtocol#openServer

this.serverMap.put(key, this.createServer(url));

以ip端口号为key,刚刚生成的HeaderExchangeServer为value加入到serverMap中

回到ProtocolListenerWrapper中

 

 new ListenerExporterWrapper(this.protocol.export(invoker), Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), "exporter.listener"))));

这个ListnerExporeterWrapper是一个成员变量是一个DubboExtproter,listner的对象

 

回到ResgistryProtocol#doLocalExport方法

  exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
            bounds.put(key, exporter);
        }
    }
}
return (ExporterChangeableWrapper<T>) exporter;

这里吧刚刚返回的ListnerExporeterWrapper封装到了一个ExporterChangeableWrapper,并放到了bounds中缓存起来,然后返回到

ResgistryProtocol#export中

final Registry registry = getRegistry(originInvoker);

这句话是zookeeper得连接

return registryFactory.getRegistry(registryUrl);直接看这句话

这个resistryFactory是一个RegistryFactory$Adaptive

package com.alibaba.dubbo.registry;

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

public class RegistryFactory$Adpative implements com.alibaba.dubbo.registry.RegistryFactory {
    public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
        if (arg0 == null) { throw new IllegalArgumentException("url == null"); }
        com.alibaba.dubbo.common.URL url = arg0;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null) {
            throw new IllegalStateException(
                "Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString()
                    + ") use keys([protocol])");
        }
        com.alibaba.dubbo.registry.RegistryFactory extension
            = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(
            com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
        return extension.getRegistry(arg0);
    }
}
    这里extName=zookeeper

这个extension=ZookeeperRegistryFactory

ZookeeperRegistryFactory得getRegistry

public Registry getRegistry(URL url) {
   url = url.setPath(RegistryService.class.getName())
         .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
         .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
   String key = url.toServiceString();
    // 锁定注册中心获取过程,保证注册中心单一实例
    LOCK.lock();
    try {
        Registry registry = REGISTRIES.get(key);
        if (registry != null) {
            return registry;
        }
        registry = createRegistry(url);
        if (registry == null) {
            throw new IllegalStateException("Can not create registry " + url);
        }
        REGISTRIES.put(key, registry);
        return registry;
    } finally {
        // 释放锁
        LOCK.unlock();
    }
}

首先会从REGISTRIES获取到zookeeper的节点信息,如果没有就会创建一个zookeeper的注册节点

registry = createRegistry(url);

这里进入ZookeeperRegistry的顶层构造函数

 AbstractRegistry(URL url) {
忽略一段代码
    this.file = file;
    loadProperties();
    notify(url.getBackupUrls());
}

loadProperties(); 把file中的配置文件加载到properties中

notify是遍历订阅的一些内容

然后是第二层构造函数

FailbackRegistry
public FailbackRegistry(URL url) {
    super(url);
    int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
    this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
        public void run() {
            // 检测并连接注册中心
            try {
                retry();
            } catch (Throwable t) { // 防御性容错
                logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
            }
        }
    }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
}

这里会定时去检测,重连注册中心,这里用到ScheduledThreadPool

然后再到ZookeeperRegistry

zkClient = zookeeperTransporter.connect(url);

这里的zookeeperTransporter是ZookeeperTransporters$Adaptve

extName=ZkClient

所以进入ZkclientZookeeperTransporter#connect

public ZookeeperClient connect(URL url) {
   return new ZkclientZookeeperClient(url);
}

这里会创建一个ZkClientZookpeerClient实例

public ZkclientZookeeperClient(URL url) {
   super(url);
   client = new ZkClient(url.getBackupAddress());
   client.subscribeStateChanges(new IZkStateListener() {
      public void handleStateChanged(KeeperState state) throws Exception {
         ZkclientZookeeperClient.this.state = state;
         if (state == KeeperState.Disconnected) {
            stateChanged(StateListener.DISCONNECTED);
         } else if (state == KeeperState.SyncConnected) {
            stateChanged(StateListener.CONNECTED);
         }
      }
      public void handleNewSession() throws Exception {
         stateChanged(StateListener.RECONNECTED);
      }
   });
}构造函数中会创建一个ZkClient,并且给这个zkClient做一些监听

回到ZookeeperRegistry构造函数

 zkClient.addStateListener(new StateListener() {
        public void stateChanged(int state) {
           if (state == RECONNECTED) {
           try {
      recover();
   } catch (Exception e) {
      logger.error(e.getMessage(), e);
   }
           }
        }
    });
}

之前返回的zkClient会加上状态监听

这时的registry的信息

回到ZookeeperRegistryFactory#getRegistry

接下去会吧这个rigistry放在REGISTRIES本地缓存中

 

回到RegistryProtocol#export方法

registry.register(registedProviderUrl);

这里会将提供者注册到zookeeper中,熟悉zookeeper的都知道,这其实是在zookeeper中创建一个节点

FailbackRegistry#register

关注doRegister(url);

点进去发现是通过之前创建的zkClient来创建一个节点

zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));

回到RegistryProtocol#export方法

registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

这里面会做一些订阅动作,点进去

关注

doUnsubscribe(url, listener);
ZookeeperRegistry#doSubscribe
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
    listeners.putIfAbsent(listener, new ChildListener() {
        public void childChanged(String parentPath, List<String> currentChilds) {
           ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
        }
    });
    zkListener = listeners.get(listener);
}
zkClient.create(path, false);
List<String> children = zkClient.addChildListener(path, zkListener);

这里会给给子节点添加监听,并且通过zkClient添加到zookeeper中

注意到childChanged中的方法是 ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));

所以我们看下ZookeeperRegistry#notify

ZookeeperRegistry的notify方法在AbstractRegistry中

其他没什么,注意有这样一句话:

saveProperties(url);

这句话就是把发生变化的节点保存在本地properties中

回到RegistryProtocol#export中

return new Exporter<T>最终会返回一个exporoter

回到ServiceConfig#doExportUrlsFor1Protocol

exporters.add(exporter);将刚刚返回的exporter加入到
exporters中

-------------------------------

花了两篇写dubbo的服务暴露,用一张图来说明服务暴露的总体架构

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值