06.Dubbo 源码解析之服务引用

1. 环境搭建

  • 代码已经上传至 https://github.com/masteryourself/dubbo ,分支名称是 masteryourself-2.7.3-release

  • provider 是 dubbo-demo-xml-provider 工程,启动类是 Application

  • consumer 是 dubbo-demo-xml-consumer 工程,启动类是 Application

2. 源码解析

2.1 关于 ServiceBean

image

  • 由于 ReferenceBean 实现了 FactoryBean 接口,所以会在 getObject 方法中返回真实的 Spring Bean 对象

2.2 流程预览

image

// 1. dubbo 与 Spring 集成,ReferenceBean 继承了 FactoryBean 接口,所以会调用 【getObject】 方法获取实现类
org.apache.dubbo.config.spring.ReferenceBean#getObject ->
	org.apache.dubbo.config.ReferenceConfig#get

		// 1.1 检查和更新属性,同之前步骤
		org.apache.dubbo.config.ReferenceConfig#checkAndUpdateSubConfigs

		// 1.2 初始化 ref 对象
		org.apache.dubbo.config.ReferenceConfig#init ->

			// 1.2.1(*) 创建代理对象
			org.apache.dubbo.config.ReferenceConfig#createProxy

				// 1.2.1.1 判断是否是 jvm 引用,判断依据就是:InjvmProtocol 中的 【exporterMap】 中是否有 url 所对应的 serviceKey
				// serviceKey:接口名 + 方法名 + version
				org.apache.dubbo.config.ReferenceConfig#shouldJvmRefer

				// 1.2.1.2 如果配置了 url(直连服务提供者),则会解析 url,添加到 urls 中,因为可以用 ; 号隔开添加多个,注意这里也可以配置 registry 开头的服务直连

				// 1.2.1.3 获取注册中心地址,构造 urls 对象(存放所有的 ),urls 结构如:
				// registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=19272
					// &qos-port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService
					// %26lazy%3Dfalse%26methods%3DsayHello%26pid%3D19272%26qos-port%3D33333%26register.ip%3D192.168.89.1%26side%3Dconsumer
					// %26sticky%3Dfalse%26timestamp%3D1577587535345&registry=zookeeper&timestamp=1577587537916
				
				// 1.2.1.4(*) 调用 refer 方法创建 invoker
				// 调用 Protocol 的动态代理类 【Protocol$Adaptive】 的 refer 方法,先经过 wrapper 包装类,再到 【RegistryProtocol】
				org.apache.dubbo.rpc.Protocol$Adaptive#refer ->
					// 当 protocol 不为 registry 时才会起作用,这里是 registry,所以没有作用
					org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#refer ->
						// 当 protocol 不为 registry 时才会起作用,这里是 registry,所以没有作用
						org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper#refer ->

							// 1.2.1.4.1 把 registry 协议改为 url 中 registry 所对应的协议,这里 zookeeper 协议,默认是 dubbo 协议,形如
							// zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=19048
								// &qos-port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService
								// %26lazy%3Dfalse%26methods%3DsayHello%26pid%3D19048%26qos-port%3D33333%26register.ip%3D192.168.89.1
								// %26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1577590285415&timestamp=1577590287612
							org.apache.dubbo.registry.integration.RegistryProtocol#refer

								// 1.2.1.4.1.1(*) 最终要的步骤
								org.apache.dubbo.registry.integration.RegistryProtocol#doRefer

									// 1.2.1.4.1.1.1 创建服务目录
									org.apache.dubbo.registry.integration.RegistryDirectory#<init>

									// 1.2.1.4.1.1.2 向 zk 注册服务消费者地址
									org.apache.dubbo.registry.RegistryService#register

									// 1.2.1.4.1.1.3 创建路由链
									org.apache.dubbo.registry.integration.RegistryDirectory#buildRouterChain ->
										// 创建路由链,然后设置到 RegistryDirectory 中
										org.apache.dubbo.rpc.cluster.RouterChain#buildChain

											// 1.2.1.4.1.1.3.1(*) 获取所有 @Activate 注解 RouterFactory 的扩展类,创建路由集合 routers,根据优先级排序
											org.apache.dubbo.rpc.cluster.RouterChain#<init>

									// 1.2.1.4.1.1.4 监听 providers、configurators、routers 三个目录
									org.apache.dubbo.registry.integration.RegistryDirectory#subscribe

									// 1.2.1.4.1.1.5 调用 Cluster 的动态代理类 【Cluster$Adaptive】 的 join 方法创建 invoker 对象,先经过 wrapper 包装类,再到 【FailoverCluster】
									org.apache.dubbo.rpc.cluster.Cluster$Adaptive#join ->
										// 创建 MockClusterInvoker,用于 在 invoker 中判断是否需要 mock
										org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper#join ->
											// 创建 FailoverClusterInvoker 对象,持有 RegistryDirectory
											org.apache.dubbo.rpc.cluster.support.FailoverCluster#join

				// 1.2.1.5 调用 ProxyFactory 的动态代理类 【ProxyFactory$Adaptive】 的 getProxy 方法创建动态代理对象,先经过 wrapper 包装类,再到 【JavassistProxyFactory】
				org.apache.dubbo.rpc.ProxyFactory$Adaptive#getProxy ->
					// 用于实现本地存根 stub 逻辑
					org.apache.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper#getProxy ->
						// 创建代理对象
						org.apache.dubbo.rpc.proxy.AbstractProxyFactory#getProxy ->
							// 使用 Javassist 技术创建动态代理对象,和 jdk 基本一致,InvocationHandler 是 dubbo 中的 【InvokerInvocationHandler】
							org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory#getProxy

2.3 流程详解

2.3.1 ReferenceConfig#createProxy(1.2.1)
  • org.apache.dubbo.config.ReferenceConfig
private T createProxy(Map<String, String> map) {

    // 判断是否是 jvm 引用,判断依据就是:InjvmProtocol 中的 【exporterMap】 中是否有 url 所对应的 serviceKey
    if (shouldJvmRefer(map)) {
        URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
        invoker = REF_PROTOCOL.refer(interfaceClass, url);
        
        ...
        
    }
    // 如果配置了 url(直连服务提供者),则会解析 url,添加到 urls 中,因为可以用 ; 号隔开添加多个
    else {
        urls.clear(); // reference retry init will add url to urls, lead to OOM
        if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
            String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
            if (us != null && us.length > 0) {
                for (String u : us) {
                    URL url = URL.valueOf(u);
                    if (StringUtils.isEmpty(url.getPath())) {
                        url = url.setPath(interfaceName);
                    }
                    // 也可以配置 registry 开头的服务直连
                    if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                    } else {
                        urls.add(ClusterUtils.mergeUrl(url, map));
                    }
                }
            }
        }
        // 创建 invoker 对象
        else { // assemble URL from register center's configuration
            
            ...
            
        }

        // 如果只有一个注册中心地址,直接取第一个调用 refer 方法创建 invoker
        // urls 中存放的对象是:
        // registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=19272&qos-port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService%26lazy%3Dfalse%26methods%3DsayHello%26pid%3D19272%26qos-port%3D33333%26register.ip%3D192.168.89.1%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1577587535345&registry=zookeeper&timestamp=1577587537916
        if (urls.size() == 1) {
            invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
        } 
        // 多个注册中心处理逻辑
        else {
            List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
            URL registryURL = null;
            
            // 遍历所有的注册中心地址,把多个 invoker 合并成一个 invoker 对象
            for (URL url : urls) {
                invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
                if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                    registryURL = url; // use last registry url
                }
            }
            if (registryURL != null) { // registry url is available
                // use RegistryAwareCluster only when register's CLUSTER is available
                URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
                // The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
                invoker = CLUSTER.join(new StaticDirectory(u, invokers));
            } else { // not a registry url, must be direct invoke.
                invoker = CLUSTER.join(new StaticDirectory(invokers));
            }
        }
    }

    ...
    
    // create service proxy
    // 调用 ProxyFactory 的动态代理类 【ProxyFactory$Adaptive】 的 getProxy 方法创建动态代理对象,先经过 wrapper 包装类,再到 【JavassistProxyFactory】
    return (T) PROXY_FACTORY.getProxy(invoker);
}
2.3.2 RegistryProtocol#refer(1.2.1.4)
  • org.apache.dubbo.registry.integration.RegistryProtocol#refer
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {

    // 把 registry 协议改为 url 中 registry 所对应的协议,这里 zookeeper 协议,默认是 dubbo 协议
    // zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=19272&qos-port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService%26lazy%3Dfalse%26methods%3DsayHello%26pid%3D19272%26qos-port%3D33333%26register.ip%3D192.168.89.1%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1577587535345&timestamp=1577587537916
    url = URLBuilder.from(url)
            .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
            .removeParameter(REGISTRY_KEY)
            .build();

    // 因为这里的协议已经变成了 zookeeper,所以这里的 registry 是 ZookeeperRegistry
    Registry registry = registryFactory.getRegistry(url);
    if (RegistryService.class.equals(type)) {
        return proxyFactory.getInvoker((T) registry, type, url);
    }

    // group 处理
    // group="a,b" or group="*"
    Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
    String group = qs.get(GROUP_KEY);
    if (group != null && group.length() > 0) {
        if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
            return doRefer(getMergeableCluster(), registry, type, url);
        }
    }
    return doRefer(cluster, registry, type, url);
}
2.3.3 RegistryProtocol#doRefer(1.2.1)
  • org.apache.dubbo.registry.integration.RegistryProtocol#doRefer
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);
    // all attributes of REFER_KEY
    Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
    URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
    if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
        directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
        // 向 zk 注册服务消费者地址,即创建 consumer 临时节点
        registry.register(directory.getRegisteredConsumerUrl());
    }
    
    // 创建路由链
    directory.buildRouterChain(subscribeUrl);
    
    // 监听 providers、configurators、routers 三个目录
    directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
            PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));

    // 调用 Cluster 的动态代理类 【Cluster$Adaptive】 的 join 方法创建 invoker 对象,先经过 wrapper 包装类,再到 【FailoverCluster】
    Invoker invoker = cluster.join(directory);
    ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
    return invoker;
}
2.3.4 RouterChain#(1.2.1.4.1.1.3.1)
  • org.apache.dubbo.rpc.cluster.RouterChain#RouterChain
private RouterChain(URL url) {
    // 获取所有 @Activate 注解 RouterFactory 的扩展类
    List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class)
            .getActivateExtension(url, (String[]) null);

    // 循环 RouterFactory,分别调用 getRoute 方法存储到集合中,这里有 4 个 route
    // 0 = {MockInvokersSelector@3011}
    // 1 = {TagRouter@3012}
    // 2 = {AppRouter@3013}
    // 3 = {ServiceRouter@3014}
    List<Router> routers = extensionFactories.stream()
            .map(factory -> factory.getRouter(url))
            .collect(Collectors.toList());

    initWithRouters(routers);
}
发布了37 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/masteryourself/article/details/103759056