Dubbo源码消费者端服务的订阅流程

关注消费者端服务的订阅,我们从ReferenceConfig中的createProxy()开始分析,我们重点先关注其中的一句,当注册中心只有一个(单个或集群)时进入此分支即执行下面这一句。(关于createProxy整个流程会在后面博文中重点介绍)

invoker = refprotocol.refer(interfaceClass, urls.get(0));
其中refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();之前关于@SPI的文章中分析过,refProtocol其实是字节码动态生成的适配类。
    public class Protocol$Adpative implements Protocol {  
        public Exporter export(Invoker invoker) throws com.alibaba.dubbo.rpc.RpcException {  
            if (invoker == null || invoker.getUrl() == null) {  
                throw new IllegalArgumentException("xxx");  
            }  
              
            URL url = invoker.getUrl();  
            String extName = url.getProtocol() == null ? "dubbo" : url.getProtocol();  
            if(extName == null) {  
                throw new IllegalStateException("xxx");  
            }  
              
            Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);  
            return extension.export(invoker);  
        }  
      
        public Invoker refer(Class cls, URL u) throws RpcException {  
            if (u == null) {  
                throw new IllegalArgumentException("url == null");  
            }  
            URL url = u;  
            String extName = url.getProtocol() == null ? "dubbo" : url.getProtocol();  
            if(extName == null) {  
                throw new IllegalStateException("xxx");  
            }  
            Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);  
            return extension.refer(cls, u);  
        }  
    }  

实际调用了其中的refer()方法,我们可以看到SPI的动态选择在这里体现,这里传入的URL的protocol为reistery,所以这里的extension实际上是registryProtocol的实例。这里调用了registryProtocol的refer()方法。

    @SuppressWarnings("unchecked")
	public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY))
                .removeParameter(Constants.REGISTRY_KEY);
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
        	return proxyFactory.getInvoker((T) registry, type, url);
        }

        // group="a,b" or group="*"
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
        String group = qs.get(Constants.GROUP_KEY);
        if (group != null && group.length() > 0 ) {
            if ( ( Constants.COMMA_SPLIT_PATTERN.split( group ) ).length > 1
                    || "*".equals( group ) ) {
                return doRefer( getMergeableCluster(), registry, type, url );
            }
        }
        return doRefer(cluster, registry, type, url);
    }

先修改url数据结构上的protocol改为registry对应的协议(比如multicast或者zookeeper),调用registryFactory的getRegister,根据url返回不同的registry对象,在初始化registry对象时,通过代理和注册中心建立连接。其中registryFactory也是个代理适配器对象(会根据url对应不同的协议实际上可能是ZookeeperRegistryFactory、MulticastRegistryFactory等)。这里采用默认的dubbo实现,其中getRegistry()方法具体逻辑在AbstractRegistryFactory中

    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();
        }
    }

先把要注册的服务跟接口都改为RegistryService(RegistryService是注册中心的接口)。这里会根据生成的注册中心服务url寻找注册中心的实例,如果已经生成过,那么直接返回,否则调用createRegister()生成新的注册中心。

默认是dubbo实现的registryFactory为DubboRegistryFactory,我们来看下具体的createRegistry()方法。

    public Registry createRegistry(URL url) {
        url = getRegistryURL(url);
        List<URL> urls = new ArrayList<URL>();
        urls.add(url.removeParameter(Constants.BACKUP_KEY));
        String backup = url.getParameter(Constants.BACKUP_KEY);
        if (backup != null && backup.length() > 0) {
            String[] addresses = Constants.COMMA_SPLIT_PATTERN.split(backup);
            for (String address : addresses) {
                urls.add(url.setAddress(address));
            }
        }
        RegistryDirectory<RegistryService> directory = new RegistryDirectory<RegistryService>(RegistryService.class,
             url.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
            .addParameterAndEncoded(Constants.REFER_KEY, url.toParameterString()));
        Invoker<RegistryService> registryInvoker = cluster.join(directory);
        RegistryService registryService = proxyFactory.getProxy(registryInvoker);
        DubboRegistry registry = new DubboRegistry(registryInvoker, registryService);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        directory.notify(urls);
        directory.subscribe(new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0,
             RegistryService.class.getName(), url.getParameters()));
        return registry;
    }

我们可以看到,这里构造了接口registryService的代理,(这里,我们可以把注册中心理解成暴露服务端,与注册中心连接的过程可以看成RegistryService的远程方法调用的过程,当然跟其他服务调用不同的是,这里注册中心作为生产者是直接与当前消费者直连,而不是在注册中心得到url,redirect访问生产者)。这里directory的subscribe实际上是调用register的subsubscribe。

    public void subscribe(URL url) {
        setConsumerUrl(url);
        registry.subscribe(url, this);
    }    
    protected void doSubscribe(URL url, NotifyListener listener) {
        registryService.subscribe(url, listener);
    }
实则调用了registryService代理的subscrible,根据之前对代理的分析我们明确知道,他是调用了registryInvoker的invoke方法,其实就是一次普通的远程调用 方法。传入参数的url中被加入了回调参数,listener是变更事件监听器,注册中心通过对这个参数的调用回调给消费者。

在得到registry之后,我们继续registryProtocol的refer()方法

我们可以看到根据url上面的group的配置,分为group模式跟非group模式,剩下都是调用doRefer(),无非在group模式时返回的invoker为MergeableClusterInvoker,非group模式时返回的invoker为FailoverClusterInvoker。

    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0,
                 type.getName(), directory.getUrl().getParameters());
        if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
                && url.getParameter(Constants.REGISTER_KEY, true)) {
            registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                    Constants.CHECK_KEY, String.valueOf(false)));
        }
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, 
                Constants.PROVIDERS_CATEGORY 
                + "," + Constants.CONFIGURATORS_CATEGORY 
                + "," + Constants.ROUTERS_CATEGORY));
        return cluster.join(directory);
    }

这里的registry就是之前的registryService,通过远程调用的方式,其中url中以接口名字为参数,来完成接口方法的订阅。在默认dubbo协议下异步等待注册中心回调,通过.join()方法得到默认的invoker,FailoverClusterInvoker即远程服务执行体。当然,注册中心需要在subscribe()方法末尾远程回调listener的notify方法,即在完成订阅后告诉消费者,达到服务地址的回调

小结

通过配置选择合适的协议,生成对应的registryService代理,通过远程回调的方式,连接上注册中心,再通过registryService代理,传递带有接口类型url数据结构,实现在注册中心的服务的订阅,得到服务地址,返回invoker。

说在最后的话

本文可能思路多多少少不是特别清楚,比较我也是边看边写的,等我把生产者服务暴露端的流程过一遍后,再把整个串起来总结一遍。


猜你喜欢

转载自blog.csdn.net/panxj856856/article/details/80527443