Dubbo源码学习11

引入服务的入口ReferenceBean。

ReferenceBean.java

除了多实现了FactoryBean接口其他的结构与ServiceBean(参考https://blog.csdn.net/qq_23536449/article/details/102639873)几乎差不多。我们知道FactoryBean是一种特殊的bean是;它是一个能生产对象的工厂Bean,它的实现和工厂模式及修饰器模式很像,在Dubbo的实现中FactoryBean接口的作用:创建代理Bean

afterPropertiesSet()

@Override
    @SuppressWarnings({"unchecked"})
    public void afterPropertiesSet() throws Exception {
        //ReferenceBean没有配置consumer,从applicationContext加载ConsumerConfig
        //获取到了进行配置好了
        if (getConsumer() == null) {
            Map<String, ConsumerConfig> consumerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class, false, false);
            if (consumerConfigMap != null && consumerConfigMap.size() > 0) {
                ConsumerConfig consumerConfig = null;
                for (ConsumerConfig config : consumerConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (consumerConfig != null) {
                            throw new IllegalStateException("Duplicate consumer configs: " + consumerConfig + " and " + config);
                        }
                        consumerConfig = config;
                    }
                }
                if (consumerConfig != null) {
                    setConsumer(consumerConfig);
                }
            }
        }
        //ApplicationConfig属性赋值
        if (getApplication() == null
                && (getConsumer() == null || getConsumer().getApplication() == null)) {
            Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
            if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
                ApplicationConfig applicationConfig = null;
                for (ApplicationConfig config : applicationConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (applicationConfig != null) {
                            throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
                        }
                        applicationConfig = config;
                    }
                }
                if (applicationConfig != null) {
                    setApplication(applicationConfig);
                }
            }
        }
        //ModuleConfig属性应用赋值
        if (getModule() == null
                && (getConsumer() == null || getConsumer().getModule() == null)) {
            Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
            if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
                ModuleConfig moduleConfig = null;
                for (ModuleConfig config : moduleConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (moduleConfig != null) {
                            throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
                        }
                        moduleConfig = config;
                    }
                }
                if (moduleConfig != null) {
                    setModule(moduleConfig);
                }
            }
        }
        //注册中心RegistryConfig应用并赋值
        if ((getRegistries() == null || getRegistries().isEmpty())
                && (getConsumer() == null || getConsumer().getRegistries() == null || getConsumer().getRegistries().isEmpty())
                && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().isEmpty())) {
            Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
            if (registryConfigMap != null && registryConfigMap.size() > 0) {
                List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
                for (RegistryConfig config : registryConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        registryConfigs.add(config);
                    }
                }
                if (registryConfigs != null && !registryConfigs.isEmpty()) {
                    super.setRegistries(registryConfigs);
                }
            }
        }
        //监控中心MonitorConfig应用并赋值
        if (getMonitor() == null
                && (getConsumer() == null || getConsumer().getMonitor() == null)
                && (getApplication() == null || getApplication().getMonitor() == null)) {
            Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
            if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
                MonitorConfig monitorConfig = null;
                for (MonitorConfig config : monitorConfigMap.values()) {
                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        if (monitorConfig != null) {
                            throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
                        }
                        monitorConfig = config;
                    }
                }
                if (monitorConfig != null) {
                    setMonitor(monitorConfig);
                }
            }
        }
        //饥饿加载判断
        Boolean b = isInit();
        if (b == null && getConsumer() != null) {
            b = getConsumer().isInit();
        }
        if (b != null && b.booleanValue()) {
            getObject();
        }
    }

该方法通过检查有没有给类似<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.study.day01.xml.service.EchoService"/>配置了ConsumerConfig、ApplicationConfig、ModuleConfig、RegistryConfig(多个)、MonitorConfig;没有的话通过在Spring的上下文容器中查找这些配置,并设置到ReferenceBean的属性里。最后根据init属性判断是否应该饥饿创建代理对象!

getObject()

@Override
    public Object getObject() throws Exception {
        return get();
    }

FactoryBean接口提供的方法;该方法用于创建代理对象。就是说我们的ReferenceBean最终创建的bean就是getObject()方法创建的bean

ReferenceConfig.get()

public synchronized T get() {
        if (destroyed) {
            throw new IllegalStateException("Already destroyed!");
        }
        //检测ref是否为null,为null调用init创建
        if (ref == null) {
            init();
        }
        return ref;
    }

init()

 private void init() {
        //已经初始化的 不需要再次初始化
        if (initialized) {
            return;
        }
        initialized = true;
        //检测接口名称是否为null
        if (interfaceName == null || interfaceName.length() == 0) {
            throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");
        }
        //检查ConsumerConfig是否配置了没有配置,创建,并根据系统环境变量配置consumer
        checkDefault();
        appendProperties(this);
        //判断是否泛化调用
        if (getGeneric() == null && getConsumer() != null) {
            setGeneric(getConsumer().getGeneric());
        }
        //判断generic字符串是否不为空并且(generic属性为true或者nativejava或者bean)
        //设置interfaceClass为GenericService
        if (ProtocolUtils.isGeneric(getGeneric())) {
            interfaceClass = GenericService.class;
        } else {
            try {
                //加载该interfaceClass类,没有找到抛出异常
                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                        .getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            //接口interfaceClass与 methodConfig的配置是否存在或者合理即,有些
            //method是否在interfaceClass中
            checkInterfaceAndMethods(interfaceClass, methods);
        }
        //加载interfaceName变量对应的系统变量
        String resolve = System.getProperty(interfaceName);
        String resolveFile = null;
        //如果系统中没有interfaceName的配置
        if (resolve == null || resolve.length() == 0) {
            //加载系统变量中的dubbo.resolve.file属性
            resolveFile = System.getProperty("dubbo.resolve.file");
            //如果系统变量没有dubbo.resolve.file
            if (resolveFile == null || resolveFile.length() == 0) {
                //加载用户系统根目录下的dubbo-resolve.properties文件
                File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
                //如果存在获取 resolveFile的绝对路径
                if (userResolveFile.exists()) {
                    resolveFile = userResolveFile.getAbsolutePath();
                }
            }
            //如果存在resolveFile读取文件中的 interfaceName属性
            if (resolveFile != null && resolveFile.length() > 0) {
                Properties properties = new Properties();
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream(new File(resolveFile));
                    properties.load(fis);
                } catch (IOException e) {
                    throw new IllegalStateException("Unload " + resolveFile + ", cause: " + e.getMessage(), e);
                } finally {
                    try {
                        if (null != fis) fis.close();
                    } catch (IOException e) {
                        logger.warn(e.getMessage(), e);
                    }
                }
                resolve = properties.getProperty(interfaceName);
            }
        }
        //如果resolve不为null
        if (resolve != null && resolve.length() > 0) {
            // 将 resolve 赋值给 url
            url = resolve;
            if (logger.isWarnEnabled()) {
                if (resolveFile != null) {
                    logger.warn("Using default dubbo resolve file " + resolveFile + " replace " + interfaceName + "" + resolve + " to p2p invoke remote service.");
                } else {
                    logger.warn("Using -D" + interfaceName + "=" + resolve + " to p2p invoke remote service.");
                }
            }
        }
        /**
         * registries和monitor的配置优先级为<dubbo:consumer的优先于<dubbo:module优先于<dubbo:application
         */
        if (consumer != null) {
            if (application == null) {
                application = consumer.getApplication();
            }
            if (module == null) {
                module = consumer.getModule();
            }
            if (registries == null) {
                registries = consumer.getRegistries();
            }
            if (monitor == null) {
                monitor = consumer.getMonitor();
            }
        }
        if (module != null) {
            if (registries == null) {
                registries = module.getRegistries();
            }
            if (monitor == null) {
                monitor = module.getMonitor();
            }
        }
        if (application != null) {
            if (registries == null) {
                registries = application.getRegistries();
            }
            if (monitor == null) {
                monitor = application.getMonitor();
            }
        }
        //检查并配置ApplicationConfg
        checkApplication();
        //检测本地存根
        checkStub(interfaceClass);
        checkMock(interfaceClass);
        Map<String, String> map = new HashMap<String, String>();
        Map<Object, Object> attributes = new HashMap<Object, Object>();
        //添加side、dubbo、timestamp、pid到map中
        map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
        map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
        if (ConfigUtils.getPid() > 0) {
            map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
        }
        //不是泛化服务调用,在map中放入revision、methods、interface信息
        if (!isGeneric()) {
            //获取版本
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
                map.put("revision", revision);
            }
            //获取interfaceClass的方法放入map
            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
            if (methods.length == 0) {
                logger.warn("NO method found in service interface " + interfaceClass.getName());
                map.put("methods", Constants.ANY_VALUE);
            } else {
                map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }
        map.put(Constants.INTERFACE_KEY, interfaceName);
        //将ApplicationConfig、ModuleConfig、ConsumerConfig、ReferenceConfig的属性值放入到map中
        appendParameters(map, application);
        appendParameters(map, module);
        appendParameters(map, consumer, Constants.DEFAULT_KEY);
        appendParameters(map, this);
        //prefix--->group/interfaceName:version
        //比如com.xxx.xxxService 或者dubbo/com.xxx.xxxService:1.2.1
        String prefix = StringUtils.getServiceKey(map);
        //如果methodConfig存在
        if (methods != null && !methods.isEmpty()) {
            //遍历MethodConfig
            for (MethodConfig method : methods) {
                //将method的属性添加methodName前缀后放到map中谢谢
                appendParameters(map, method, method.getName());
                //map中是否有methodName.retry;删除掉并用methodName.retries代替这种配置
                String retryKey = method.getName() + ".retry";
                if (map.containsKey(retryKey)) {
                    String retryValue = map.remove(retryKey);
                    //将
                    if ("false".equals(retryValue)) {
                         添加重试次数配置 methodName.retries = 0
                        map.put(method.getName() + ".retries", "0");
                    }
                }
                // 添加 MethodConfig 中的“属性”字段放入 attributes
                // 比如 onreturn、onthrow、oninvoke 等
                appendAttributes(attributes, method, prefix + "." + method.getName());
                //attributes的key com.gysoft.xxx.XxxService.hello.onreturn.method  value 方法对象
                checkAndConvertImplicitConfig(method, map, attributes);
            }
        }
        //服务消费者的ip地址
        String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
        if (hostToRegistry == null || hostToRegistry.length() == 0) {
            //获取本机地址
            hostToRegistry = NetUtils.getLocalHost();
        } else if (isInvalidLocalHost(hostToRegistry)) {
            throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
        }
        //map中放入register.ip属性 值为消费者的ip
        map.put(Constants.REGISTER_IP_KEY, hostToRegistry);
        //将attributes放入 system context对象中
        StaticContext.getSystemContext().putAll(attributes);
        //创建代理
        ref = createProxy(map);
        /**
         * 创建一个消费者模型对象
         * 主要为了保存消费方的名称、消费方的配置元数据、ref、接口方法
         * 然后放入到ApplicationModel的一个map中
         */
        ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
        ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
    }
  • a.初始化状态判断
  • b.ConsumerConfig是否配置判断,没配置通过系统变量(System.getProperty()或者指定目录下的配置文件)初始化
  • c.ReferenceConfig通过系统变量(System.getProperty()或者指定目录下的配置文件)初始化,感觉这是为了兼容1.0的版本
  • d.是否泛化调用判断处理,从系统属性或配置文件中加载与接口名相对应的配置,并将解析结果赋值给 url 字段。url 字段的作用一般是用于点对点调用。
  • e.ConsumerConfig、MonitorConfig、RegistryConfig配置根据优先级初始化
  • f.检查核心配置类是否为空,为空则尝试从其他配置类中获取
  • g.收集消费者配置到map中,还包括对MethodConfig 实例的处理。该实例包含了事件通知配置,比如 onreturn、onthrow、oninvoke 等
  • i.消费者ip的获取
  • j.封装一个提供者模型对象ConsumerModel对象,并加入缓存,各位同学可以通过ApplicationModel的相关get方法获得ConsumerModel,感兴趣的可以研究下吧。

createProxy(Map<String, String> map)

private T createProxy(Map<String, String> map) {
        URL tmpUrl = new URL("temp", "localhost", 0, map);
        final boolean isJvmRefer;
        //本地jvm引用配置
        if (isInjvm() == null) {
            // url 配置被指定,则不做本地引用
            if (url != null && url.length() > 0) {
                isJvmRefer = false;
                // 根据 url 的协议、scope 以及 injvm 等参数检测是否需要本地引用
                // 比如如果用户显式配置了 scope=local,此时 isInjvmRefer 返回 true
            } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
                isJvmRefer = true;
            } else {
                isJvmRefer = false;
            }
        } else {
            //injvm配置
            isJvmRefer = isInjvm().booleanValue();
        }
        //本地jvm引用
        if (isJvmRefer) {
            //new一个本地引用url injvm协议的并将Map的参数添加到URL对象中
            URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
            //创建invoker实例
            invoker = refprotocol.refer(interfaceClass, url);
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + interfaceClass.getName());
            }
        } else {
            //url不为空,指定的URL可能是对点对直连地址,也可能是注册中心URL
            if (url != null && url.length() > 0) {
                //按照;切分url字符串(因为url可能配置多个!!!)
                String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
                if (us != null && us.length > 0) {
                    for (String u : us) {
                        //将字符串转换为URL对象
                        URL url = URL.valueOf(u);
                        //设置接口全限定名称的url
                        if (url.getPath() == null || url.getPath().length() == 0) {
                            url = url.setPath(interfaceName);
                        }
                        //检测 url 协议是否为 registry,若是,表明用户想使用指定的注册中心
                        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                            //将map变成?key=value&key2=value2这种的,然后作为refer属性的值添加到url中
                            urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
                            //合并 url,移除服务提供者的一些配置(这些配置来源于用户配置的url属性)
                            //比如线程池相关配置。并保留服务提供者的部分配置,比如版本,group,时间戳等
                            //后将合并后的配置设置为 url 查询字符串中。
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else {
                //加载注册中心url
                //registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-consumer&dubbo=2.0.2&
                // pid=16304&registry=zookeeper&timestamp=1573441590938
                List<URL> us = loadRegistries(false);
                if (us != null && !us.isEmpty()) {
                    for (URL u : us) {
                        //加载监控中心配置,赞不关心
                        URL monitorUrl = loadMonitor(u);
                        if (monitorUrl != null) {
                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                        //添加refer参数到url中
                        urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    }
                }
                if (urls.isEmpty()) {
                    throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
                }
            }
            //单个注册中心的服务提供者
            if (urls.size() == 1) {
                //调用RegistryProtocol的refer方法创建invoker
                invoker = refprotocol.refer(interfaceClass, urls.get(0));
            //多个注册中心或多个服务提供者,或者两中情况都存在
            } else {
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                //获取所有Invoker
                for (URL url : urls) {
                    invokers.add(refprotocol.refer(interfaceClass, url));
                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // use last registry url
                    }
                }
                //如果注册中心的url是没问题的;使用AvaliableCluster
                if (registryURL != null) { // registry url is available
                    // use AvailableCluster only when register's cluster is available
                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                } else { // not a registry url
                    invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }

        Boolean c = check;
        if (c == null && consumer != null) {
            c = consumer.isCheck();
        }
        if (c == null) {
            c = true; // default true
        }
        //如果要检查提供者是否存在但是invoker不可用,抛出异常
        if (c && !invoker.isAvailable()) {
            // make it possible for consumer to retry later if provider is temporarily unavailable
            initialized = false;
            throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
        }
        if (logger.isInfoEnabled()) {
            logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
        }
        // create service proxy
        // 生成代理类
        return (T) proxyFactory.getProxy(invoker);
    }
  • 1.是否为本地jvm引用判断,如果本地jvm引用则最终会通过InJvmProtocol.refer方法创建InJvmInvoker
  • 2.在暴露invoker前对url的处理:首先是直接配置了服务引用的url的处理,然后是通过注册中心对服务引用的处理
  • 3.对服务引用的urls大小进行判断,如果等于1通过 Protocol 自适应拓展类构建 Invoker 实例接口,若 urls 元素数量大于1,即存在多个注册中心或服务直连 url,此时先根据 urls构建 Invoker列表,最后使用cluster合并多个invoker成为1个。
  • 4.服务引用的可用性判断
  • 5.使用proxyFactory创建代理对象

在上篇我们分析了ReferenceConfig.createProxy(),该方法通过调用refprotocol.refer(interfaceClass,url)创建Invoker实例;根据dubbo的spi自适应扩展机制我们不难推测出refprotocol类型为Protocol$Adaptive

Protocol$Adaptive.refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1)

public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        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.refer(arg0, arg1);
    }

经过dubbo的spi自适应扩展机制后Protocol被包装为ProtocolListenerWrapper如下,然后按如下顺序执行protocol的refer方法

ProtocolListenerWrapper.refer(Class<T> type, URL url)

QosProtocolWrapper.refer(Class<T> type, URL url)

该方法开启了qos服务器

ProtocolFilterWrapper..refer(Class<T> type, URL url)

RegistryProtocol.refer(Class<T> type, URL url)

@Override
    @SuppressWarnings("unchecked")
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        //获取url中的registry属性的值没有则为dubbo,然后设置协议为registry对应的值,处理后的url类似如下这个样子
        //对于通过点对点直连方式的url会变成dubbo://xxxx这种形式
        //zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-consumer&dubbo=2.0.2&pid=16684&
        // refer=application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
        // interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=16684&
        // register.ip=169.254.22.149&side=consumer&timestamp=1573450786523&registry=zookeeper&timestamp=1573450792295
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
        //获取到的为ZookeeperRegistry
        Registry registry = registryFactory.getRegistry(url);
        // 如果是RegistryService
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }

        // url中的查询字符串转换成map
        // refer=application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
        // interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=16684&
        // register.ip=169.254.22.149&side=consumer&timestamp=1573450786523&registry=zookeeper&timestamp=1573450792295
        // 变成了Map qs
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
        // 获取group如果为null
        String group = qs.get(Constants.GROUP_KEY);
        if (group != null && group.length() > 0) {
            //如果存在多个group或者group为* group="a,b" or group="*"
            //则使用MergeableCluster
            if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                    || "*".equals(group)) {
                return doRefer(getMergeableCluster(), registry, type, url);
            }
        }
        //只有一个组或者没有组配置,根据dubbo的spi机制默认情况下为FailoverCluster,执行doRefer
        return doRefer(cluster, registry, type, url);
    }

首先是对于url协议的处理,毕竟有些url是通过直连(不经过注册中心)方式调用,然后是获取注册中心ZookeeperRegistry(当然也有可能为DubboRegistry由于工作中很少用,这里不做分析),然后根据是否为多group选择不同的cluster进行doCluster方法的调用,关于cluster的用处放在后续的博客进行分析

doRefer(Cluster cluster, Registry registry, Class<T> type, URL url)

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        //创建RegistryDirectory
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        // 将所有refer属性放入到map
        // refer=application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
        // interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=16684&
        // register.ip=169.254.22.149&side=consumer&timestamp=1573450786523&registry=zookeeper&timestamp=1573450792295
        Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
        //consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer&check=false
        // &dubbo=2.0.2&echo.async=true&echo.retries=3&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService
        // &methods=echo,addListener&pid=16244&side=consumer&timestamp=1573451795393
        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
        if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
                && url.getParameter(Constants.REGISTER_KEY, true)) {
            //给subscrbeUrl添加category属性和check属性
            //consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer&
            //category=consumers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
            //interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=23400&
            // side=consumer&timestamp=1573451977816
            URL registeredConsumerUrl = getRegisteredConsumerUrl(subscribeUrl, url);
            registry.register(registeredConsumerUrl);
            //设置directory的registered属ConsumerUrl属性
            directory.setRegisteredConsumerUrl(registeredConsumerUrl);
        }
        //subscribeUrl添加category属性provder,configurators,routers
        //并调用RegistryDirectory的subscribe方法
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
                Constants.PROVIDERS_CATEGORY
                        + "," + Constants.CONFIGURATORS_CATEGORY
                        + "," + Constants.ROUTERS_CATEGORY));

        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }

该方法创建了RegistryDirectory对象,,然后生成服务者消费者URL对象,并向注册中心进行注册该节点,注册完毕后,调用RegistryDirectory对象的subscribe方法订阅providers、configurators、routers节点下的数据。由于一个服务可能部署在多台服务器上,这样就会在 providers 产生多个节点,这个时候就需要 Cluster 将多个服务节点合并为一个,并生成一个 Invoker,最后通过调用ProviderConsumerRegTable.registerConsumer方法将与该消费者有关的数据封装为ConsumerInvokerWrapper对象,并放入到内存中

public static void registerConsumer(Invoker invoker, URL registryUrl, URL consumerUrl, RegistryDirectory registryDirectory) {
        ConsumerInvokerWrapper wrapperInvoker = new ConsumerInvokerWrapper(invoker, registryUrl, consumerUrl, registryDirectory);
        String serviceUniqueName = consumerUrl.getServiceKey();
        Set<ConsumerInvokerWrapper> invokers = consumerInvokers.get(serviceUniqueName);
        if (invokers == null) {
            consumerInvokers.putIfAbsent(serviceUniqueName, new ConcurrentHashSet<ConsumerInvokerWrapper>());
            invokers = consumerInvokers.get(serviceUniqueName);
        }
        invokers.add(wrapperInvoker);
    }

RegistryDirectory.java

  • Directory:目录接口抽象,提供根据路由规则返回Invoker列表的能力
public interface Directory<T> extends Node {

    /**
     * get service type.
     * 服务的类型
     *
     * @return service type.
     */
    Class<T> getInterface();

    /**
     * list invokers.
     * invokers列表
     *
     * @return invokers
     */
    List<Invoker<T>> list(Invocation invocation) throws RpcException;

}
  • AbstractDirectory:目录的抽象实现:从此目录的列表方法返回的调用者列表已由路由器过滤
/**
     * 注册中心url
     * zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-consumer&dubbo=2.0.2&pid=49676&
     * refer=application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
     * interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=49676&
     * register.ip=169.254.22.149&side=consumer&timestamp=1573561006651&timestamp=1573561007009
     */
    private final URL url;

    private volatile boolean destroyed = false;
    /**
     * 消费方的url对象
     * consumer://xxxx这种
     */
    private volatile URL consumerUrl;
    /**
     * dubbo路由机制实现,通过订阅routes节点route://xxx
     * 解析而成
     */
    private volatile List<Router> routers;
  • NotifyListener:实现该接口的类能够接收到zookeeper的节点数据变动通知
  • RegistryDirectory:注册中心的目录实现,服务目录中存储了一些和服务提供者有关的信息,通过服务目录,服务消费者可获取到服务提供者的信息,比如 ip、端口、服务协议等。通过这些信息,服务消费者就可通过 Netty 等客户端进行远程调用。在一个服务集群中,服务提供者数量并不是一成不变的,如果集群中新增了一台机器,相应地在服务目录中就要新增一条服务提供者记录。或者,如果服务提供者的配置修改了,服务目录中的记录也要做相应的更新。
/**
     * 集群策略,默认为failover。
     */
    private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
    /**
     * 路由工厂,可以通过监控中心或治理中心配置。
     */
    private static final RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
    /**
     * 配置实现工厂类
     */
    private static final ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getAdaptiveExtension();
    /**
     * 服务key,默认为服务接口名。com.alibaba.dubbo.registry.RegistryService,注册中心在Dubbo中也是使用服务暴露。
     */
    private final String serviceKey; // Initialization at construction time, assertion not null
    /**
     * 服务提供者接口类,例如interface com.alibaba.dubbo.demo.DemoService
     */
    private final Class<T> serviceType; // Initialization at construction time, assertion not null
    /**
     * 服务消费者URL中的所有属性。
     */
    private final Map<String, String> queryMap; // Initialization at construction time, assertion not null
    /**
     * 注册中心URL,只保留消息消费者URL查询属性,也就是queryMap。
     */
    private final URL directoryUrl; // Initialization at construction time, assertion not null, and always assign non null value
    /**
     * 引用服务提供者方法数组。
     */
    private final String[] serviceMethods;
    /**
     * 多个服务组
     */
    private final boolean multiGroup;
    /**
     * 协议
     */
    private Protocol protocol; // Initialization at the time of injection, the assertion is not null
    /**
     * 注册中心
     */
    private Registry registry; // Initialization at the time of injection, the assertion is not null
    private volatile boolean forbidden = false;

    private volatile URL overrideDirectoryUrl; // Initialization at construction time, assertion not null, and always assign non null value

    private volatile URL registeredConsumerUrl;

    /**
     * override rules
     * Priority: override>-D>consumer>provider
     * Rule one: for a certain provider <ip:port,timeout=100>
     * Rule two: for all providers <* ,timeout=5000>
     */
    /**
     * 配置信息。
     */
    private volatile List<Configurator> configurators; // The initial value is null and the midway may be assigned to null, please use the local variable reference

    // Map<url, Invoker> cache service url to invoker mapping.
    /**
     * 服务URL对应的Invoker(服务提供者调用器)。
     */
    private volatile Map<String, Invoker<T>> urlInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
    /**
     * methodInvokerMap; methodName : List< Invoker< T >>,
     * dubbo:method 对应的Invoker缓存表。
     */
    // Map<methodName, Invoker> cache service method to invokers mapping.
    private volatile Map<String, List<Invoker<T>>> methodInvokerMap; // The initial value is null and the midway may be assigned to null, please use the local variable reference
    /**
     * 缓存invokerUrls到invokers的映射
     */
    // Set<invokerUrls> cache invokeUrls to invokers mapping.
    private volatile Set<URL> cachedInvokerUrls; // The initial value is null and the midway may be assigned to null, please use the local variable reference
public RegistryDirectory(Class<T> serviceType, URL url) {
        super(url);
        //参数校验
        if (serviceType == null)
            throw new IllegalArgumentException("service type is null.");
        if (url.getServiceKey() == null || url.getServiceKey().length() == 0)
            throw new IllegalArgumentException("registry serviceKey is null.");
        this.serviceType = serviceType;
        //获取注册中心URL的serviceKey:com.alibaba.dubbo.registry.RegistryService。
        this.serviceKey = url.getServiceKey();
        //获取zookeeper://xxx中的refer属性的值,应该像如下这样子
        //application=echo-consumer&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3&
        //interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&
        // pid=24572&register.ip=169.254.22.149&side=consumer&timestamp=1573453015233
        this.queryMap = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
        //overrideDirectoryUrl和directoryUrl属性赋值;移除注册中心URL的监控中心以及其他属性值,只保留消息消费者的配置属性。
        //zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=echo-consumer&check=false&
        //dubbo=2.0.2&echo.async=true&echo.retries=3&interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&
        //methods=echo,addListener&pid=24924&register.ip=169.254.22.149&side=consumer&timestamp=1573453320189
        this.overrideDirectoryUrl = this.directoryUrl = url.setPath(url.getServiceInterface()).clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY);
        //获取directoryUrl中的group属性
        String group = directoryUrl.getParameter(Constants.GROUP_KEY, "");
        //是否为多组配置
        this.multiGroup = group != null && ("*".equals(group) || group.contains(","));
        //获取methods属性,都是方法名称
        String methods = queryMap.get(Constants.METHODS_KEY);
        //数组
        this.serviceMethods = methods == null ? null : Constants.COMMA_SPLIT_PATTERN.split(methods);
    }

RegistryDirectory的构造方法并没有做很特别的事情呢,都是给自身的成员变量进行初始化:比如serviceType、serviceKey、queryMap、overrideDirectoryUrl、directoryUrl、multiGroup、serviceMethods。

getRegisteredConsumerUrl(subscribeUrl, url)

public URL getRegisteredConsumerUrl(final URL consumerUrl, URL registryUrl) {
        return consumerUrl.addParameters(CATEGORY_KEY, CONSUMERS_CATEGORY,
                CHECK_KEY, String.valueOf(false));
    }

给subscribeUrl添加category和check属性,添加后的registeredConsumerUrl类似:consumer://169.254.22.149/com.alibaba.dubbo.study.day01.xml.service.EchoService?application=echo-consumer& category=consumers&check=false&dubbo=2.0.2&echo.async=true&echo.retries=3& interface=com.alibaba.dubbo.study.day01.xml.service.EchoService&methods=echo,addListener&pid=23400& side=consumer&timestamp=1573451977816

ZookeeperRegistry.register(registeredConsumerUrl)

该方法我们在服务暴露已经分析过了,执行后,创建节点consumer,如下图

未完待续~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值