调试dubbo-3.服务引用

  1. 服务引用时机

    饿汉式:ReferenceBean实现了InitializingBean,afterPropertiesSet会在spring容器启动的时候调用,其中的isInit方法返回的init是<dubbo:reference />标签中的init配置的,为true的时候才会加载提供服务的类。

    懒汉式:实现了FactoryBean接口的类都是懒加载的(除实现子接口SmartFactoryBean并指定isEagerInit为true), spring启动阶段加载的bean都是懒加载的,所以提供服务的类没有被用到的话,都不会加载,只有真正用到的时候才会去加载,例如需要被注入到其他bean中的时候。

    public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
          
          
    	// ......省略
      
        @SuppressWarnings({
          
          "unchecked"})
        public void afterPropertiesSet() throws Exception {
          
          
            // ......省略
            Boolean b = isInit();
            if (b == null && getConsumer() != null) {
          
          
                b = getConsumer().isInit();
            }
            if (b != null && b.booleanValue()) {
          
          
                getObject();
            }
        }
        
        // ......省略
    }
    
  2. 创建服务引用类主体流程

    当用到bean的时候,spring对实现FactoryBean的类通过getObject获取真实引用的bean,此时会进入创建引用类的过程。

    public class ReferenceConfig<T> extends AbstractReferenceConfig {
          
          
    	// ......省略
        public Object getObject() throws Exception {
          
          
            return get();
        }
        
        public synchronized T get() {
          
          
    		// ......省略
            if (ref == null) {
          
          
                init();
            }
            return ref;
        }
        
        private void init() {
          
          
            // ......省略
            ref = createProxy(map);
            ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
            ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
        }
    }
    

    createProxy方法如下

    private T createProxy(Map<String, String> map) {
          
          
        // ......省略
        if (isJvmRefer) {
          
          
            URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
            invoker = refprotocol.refer(interfaceClass, url);
        } else {
          
          
            // 如果<dubbo:reference />标签中指定了直连模式时的url
            if (url != null && url.length() > 0) {
          
           
                // ......省略,对直连模式时的url做一些处理,并加入urls中
                if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
          
          
                    urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                } else {
          
          
                    urls.add(ClusterUtils.mergeUrl(url, map));
                }
            } else {
          
           
                // 加载注册中心配置
                List<URL> us = loadRegistries(false);
                for (URL u : us) {
          
          
                    // ......省略, 补充url参数等
                    urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                }
            }
            // 如果单注册中心
            if (urls.size() == 1) {
          
          
                invoker = refprotocol.refer(interfaceClass, urls.get(0));
            } else {
          
           // 如果有多个注册中心,每个注册中心创建一个invoker
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {
          
          
                    invokers.add(refprotocol.refer(interfaceClass, url));
                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
          
          
                        registryURL = url; 
                    }
                }
                if (registryURL != null) {
          
           
                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                } else {
          
           
                    invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }
    	// ......省略
        return (T) proxyFactory.getProxy(invoker);
    }
    

    判断,如果是本地引用,此时url形式大致为:injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=24892&qos.port=33333&register.ip=10.10.10.10&side=consumer&timestamp=1599137706187

    SPI根据url中的信息injvm,加载InjvmProtocol,调用它的refer方法,直接创建一个InjvmInvoker返回。

    public class InjvmProtocol extends AbstractProtocol implements Protocol {
          
          
    	// ......省略
        public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
          
          
            return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
        }
    }
    

    如果是远程引用,先判断如果指定了直连url,对url补充部分参数后加入ReferenceConfig的urls属性中。没有指定直连url的时候,加载注册中心的配置,对注册中心的url补充参数后加入ReferenceConfig的urls属性中。再看引入服务的urls数量,若 urls 元素数量大于1,即存在多个注册中心或服务直连 url,此时先根据 url 构建 Invoker。然后再通过 Cluster 合并多个 Invoker,最后调用 ProxyFactory 生成代理类。

    如果url是注册中心,则通过SPI加载RegistryProtocol类,调用refer方法服务引用。

  3. 基于注册中心的服务引用,创建Invoker

    上面根据注册中心的url通过SPI加载RegistryProtocol类后,调用refer方法引用服务,内部调用doRefer

    public class RegistryProtocol implements Protocol {
          
          
        // ......省略
        public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
          
          
            // ......省略
            return doRefer(cluster, registry, type, url);
        }
        
        private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
          
          
            RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
            // ......省略
                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));
    		
            Invoker invoker = cluster.join(directory);
            ProviderConsumerRegTable.registerConsuemr(invoker, url, subscribeUrl, directory);
            return invoker;
        }
        // ......省略
    }
    

    doRefer中的流程与上一篇文章服务导出流程比较像,先是到注册中心注册消费者url,形式大致如下:consumer://10.10.10.10/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9056&qos.port=33333&side=consumer&timestamp=1599182506700

    然后订阅注册中心的其他信息。完成订阅后,RegistryDirectory 会收到这几个节点下的子节点信息。由于一个服务可能部署在多台服务器上,这样就会在 providers 产生多个节点,这个时候就需要 Cluster 将多个服务节点合并为一个,并生成一个 Invoker。

  4. 创建完Invoker后,包装Invoker,生成引用类的代理

    也就是createProxy的最后一个步骤

    private T createProxy(Map<String, String> map) {
          
          
    	// ......省略
        return (T) proxyFactory.getProxy(invoker);
    }
    

    这里,代理工厂通过SPI加载默认的 JavassistProxyFactory, 将invoker包装成InvokerInvocationHandler,再传给一个代理对象再次包装。以后凡是调用引入服务的地方,引用的都是这个代理类。

    @SPI("javassist")
    public interface ProxyFactory {
          
          
    
    public class JavassistProxyFactory extends AbstractProxyFactory {
          
          
    
        @SuppressWarnings("unchecked")
        public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
          
          
            return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
        }
    }
    

猜你喜欢

转载自blog.csdn.net/u013041642/article/details/108154420