第8章 Dubbo 服务引用流程的设计与实现

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <!-- 应用名,通常与 artifactId 相同即可 -->
    <dubbo:application name="demo-consumer"/>
    <!-- 注册中心 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <!-- 生成远端服务的代理对象, 之后可以向调用本地服务一样调用远端服务 -->
    <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>
</beans>
服务消费者创建服务代理
// demoService 是代理对象,代理对象是 DemoService 接口的实现类
DemoService demoService = (DemoService) context.getBean("demoService");

一、服务消费者创建服务代理简图

Alt pic
总体流程:(默认配置情况下)
  1. 首先 ReferenceConfig 类的 init 方法调用 Protocol#refer 方法生成 Invoker 实例(如上图中的红色部分),这是服务消费的关键。
  2. 然后使用 JavassistProxyFactory#getProxy 生成接口(DemoService)的代理对象 ref
image.png
  • 服务引用第一步,有注册中心的情况下(最常用)会调用 RegistryProtocol#refer(Class<T> type, URL url) ,RegistryProtocol 实际上是其他具体 Protocol(eg. DubboProtocol)的 AOP 类,在 refer(...) 中:
  1. 获取注册中心
  2. 创建 RegistryDirectory(AOP)
  3. 首先会获取注册中心 Registry,然后进行服务注册;(AOP)
  4. 订阅providers、configurators、routers

4.1. 做第一次服务发现,获取到 provider 节点(provider 以 URL 进行表示)后;(AOP)
4.2. 之后使用具体的 DubboProtocol 将这些表示 provider 的 URL 转化为 DubboInvoker,并且为每一个 provider 创建 nettyClient,与 nettyServer 进行连接具体的 Protocol 做的事
4.3、进行 DubboInvoker 的缓存(AOP),其中 RegistryDirectory#Map<String, List<Invoker<T>>> methodInvokerMap 是后续发起调用时获取 Invoker 的真正容器(重要

  1. 将directory封装成一个ClusterInvoker(MockClusterInvoker)。
public class RegistryProtocol implements Protocol {
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        ...
        // 1. 获取注册中心:创建ZkClient实例,连接zk
        Registry registry = registryFactory.getRegistry(url);
        ...
        // 2.
        return doRefer(cluster, registry, type, url);
    }

    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        // 1. 创建 RegistryDirectory 实例
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        ...
        // 2. 向注册中心注册服务
        registry.register(registeredConsumerUrl);
        // 3. 订阅providers、configurators、routers(订阅时,调用了具体的Protocol,eg. DubboProtocol 的 refer(RegistryDirectory),方法,在该方法中,创建了 nettyClient 端,并建立了长连接)
        directory.subscribe(subscribeUrl.addParameter("category","providers,configurators,routers"));
        // 4. 将directory封装成一个ClusterInvoker(MockClusterInvoker)
        Invoker invoker = cluster.join(directory);
        ...
        return invoker;
    }
}

大致看下后续发起调用时,是怎么从 RegistryDirectory 中获取可执行的 Invoker 的。

public class RegistryDirectory<T> extends AbstractDirectory<T> implements NotifyListener {
    // 以注释处的例子为例,初始化之后:{"sayHello":[A,B], "sayBye":[B], "*":[router过滤后的provider]}
    private volatile Map<String, List<Invoker<T>>> methodInvokerMap;
    
    /************************* 初始化更新 newMethodInvokerMap *************************/
    // 订阅providers、configurators、routers时,执行的通知逻辑
    @Override
    public synchronized void notify(List<URL> urls) {
        // 服务提供者URL
        List<URL> invokerUrls = new ArrayList<URL>();
        ...
        // 初始化 invokerUrls
        for (URL url : urls) {
            String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
            ...
            if (Constants.PROVIDERS_CATEGORY.equals(category)) {
                invokerUrls.add(url);
            } 
        }
        ...
        // 只针对 providers 进行调用
        refreshInvoker(invokerUrls);
    }

    private void refreshInvoker(List<URL> invokerUrls) {
        ...
        // Translate url list to Invoker map => 将url转换为InvokerDelegate
        Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);
        // Change method name to map Invoker Map => 构造{"sayHello":InvokerDelegate}这样的键值对
        Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); 
        ...
        // 初始化实例属性 methodInvokerMap
        this.methodInvokerMap = newMethodInvokerMap;
        ...
    }

    private Map<String, List<Invoker<T>>> toMethodInvokers(Map<String, Invoker<T>> invokersMap) {
        Map<String, List<Invoker<T>>> newMethodInvokerMap = new HashMap<String, List<Invoker<T>>>();
        ...
        for (Invoker<T> invoker : invokersMap.values()) {
            // 1. 获取 provider 的所有方法 methods=xxx,yyy,zzz
            // (同一个接口的不同 provider 的 methods 参数可能不同,例如 DemoService#sayHello() 在 providerA 中有,后续添加了 DemoService#sayBye() 之后,部署到了 providerB,
            // 此时 providerA 还没部署,这一时刻,进行的服务发现根据 serviceKey 会发现 providerA 和 providerB,但是二者所拥有的方法却是不同的,那么经过如下逻辑后,
            // newMethodInvokerMap={"sayHello":[A,B], "sayBye":[B]})
            String parameter = invoker.getUrl().getParameter(Constants.METHODS_KEY);
            String[] methods = Constants.COMMA_SPLIT_PATTERN.split(parameter);
            for (String method : methods) {
                ...
                newMethodInvokerMap.put(method, methodInvokers);
                ...
            }
        }
        // newMethodInvokerMap={"sayHello":[A,B], "sayBye":[B], "*":[router过滤后的provider]}
        newMethodInvokerMap.put(Constants.ANY_VALUE, newInvokersList);
        ...
        return Collections.unmodifiableMap(newMethodInvokerMap);
    }

    /************************* 从 newMethodInvokerMap 中选择 Invoker *************************/
    @Override
    public List<Invoker<T>> doList(Invocation invocation) {
        ...
        List<Invoker<T>> invokers = null;
        Map<String, List<Invoker<T>>> localMethodInvokerMap = this.methodInvokerMap; // local reference
        if (localMethodInvokerMap != null && localMethodInvokerMap.size() > 0) {
            String methodName = RpcUtils.getMethodName(invocation);
            ...
            // 1. 根据方法名进行 Invoker 的获取:从 {"sayHello":List<InvokerDelegate实例>} 中根据 methodName("sayHello")获取List<InvokerDelegate实例>
            if (invokers == null) {
                invokers = localMethodInvokerMap.get(methodName); 
            }
            // 2. 根据 key=* 进行获取 List<Invoker>
            if (invokers == null) {
                invokers = localMethodInvokerMap.get(Constants.ANY_VALUE); 
            }
            // 3. 遍历获取一个 List<Invoker>
            if (invokers == null) {
                Iterator<List<Invoker<T>>> iterator = localMethodInvokerMap.values().iterator(); 
                if (iterator.hasNext()) {
                    invokers = iterator.next();
                }
            }
        }
        return invokers == null ? new ArrayList<Invoker<T>>(0) : invokers;
    }
    ...
}

Q: 为什么要按照方法名进行 provider 的缓存而不是直接按照 interfaceName/group/version 这样的格式?
A: 同一个接口的不同 provider 的 methods 参数可能不同,例如 DemoService#sayHello() 在 providerA 中有,后续添加了 DemoService#sayBye() 之后,部署到了 providerB,同时将新包(包含sayBye())打包给 consumer,consumer 部署完成后,假设此时 providerA 还没部署,这一时刻,consumer 进行的服务发现根据 serviceKey 会发现 providerA 和 providerB,但是二者所拥有的方法却是不同的,那么经过如下逻辑后,newMethodInvokerMap={"sayHello":[A,B], "sayBye":[B]}),这样后续如果执行 DemoService#sayBye() ,就会直接获取到 B。
注意:在 2.7.x 中去掉了 newMethodInvokerMap 属性,不再使用方法名作为 key,直接存储 List<Invoker>,代码虽然简化了,但是也丢失了 A 中描述的好处。

服务引用的第二步,见下文第二小节分析和 第10章 Dubbo 代理层的设计与实现

二、服务消费者创建服务代理源码梯形图

ReferenceConfig.init()
-->createProxy(Map<String, String> map)
  //一 获取Invoker
  -->RegistryProtocol.refer(Class<T> type, URL url)
    //1 获取注册中心:创建ZkClient实例,连接zk
    -->Registry registry = registryFactory.getRegistry(url)
      -->AbstractRegistryFactory.getRegistry(URL url)
        -->ZookeeperRegistryFactory.createRegistry(URL url)
          -->new ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter)
            -->ZkclientZookeeperTransporter.connect(URL url)
              -->new ZkclientZookeeperClient(URL url)
                -->new ZkClient(url.getBackupAddress())
            -->AbstractRegistryFactory.Map<String, Registry> REGISTRIES.put("zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService", 上边的ZookeeperRegistry实例)
    -->doRefer(Cluster cluster, Registry registry, Class<T> type, URL url)
      -->new RegistryDirectory<T>(type, url)
      //2 向注册中心注册服务
      -->registry.register(url)
        -->ZookeeperRegistry.doRegister(URL url)
          -->AbstractZookeeperClient.create(String path, boolean ephemeral)
    //3 订阅providers、configurators、routers
      -->RegistryDirectory.subscribe(URL url)
        -->ZookeeperRegistry.doSubscribe(final URL url, final NotifyListener listener)
          //3.1 会获取当前节点下已经存在的子节点(第一次服务发现发生在这里),添加子节点变化监听器
          -->List<String> children = zkClient.addChildListener(path, zkListener)
          -->AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls)
            -->saveProperties(url)
            -->RegistryDirectory.notify(List<URL> urls)
              //仅仅针对的是providers
              -->refreshInvoker(List<URL> invokerUrls)
                -->toInvokers(List<URL> urls)
                  -->ProtocolFilterWrapper.refer(Class<T> type, URL url)
                    -->DubboProtocol.refer(Class<T> serviceType, URL url)
                      //3.1.1 创建ExchangeClient,对第一次服务发现providers路径下的相关url建立长连接
                      -->getClients(URL url)
                        -->getSharedClient(URL url)
                          -->ExchangeClient exchangeClient = initClient(url)
                            -->Exchangers.connect(url, requestHandler)
                              -->HeaderExchanger.connect(URL url, ExchangeHandler handler)
                                -->new DecodeHandler(new HeaderExchangeHandler(handler)))
                                  -->Transporters.connect(URL url, ChannelHandler... handlers)
                                    -->NettyTransporter.connect(URL url, ChannelHandler listener)
                                      -->new NettyClient(url, listener)
                                        -->new MultiMessageHandler(HeartbeatHandler(AllChannelHandler(handler)))
                                        -->getChannelCodec(url)//获取Codec2,这里是DubboCountCodec实例
                                        -->doOpen()//开启netty客户端
                                        -->doConnect()//连接服务端,建立长连接
                                -->new HeaderExchangeClient(Client client, boolean needHeartbeat)//上述的NettyClient实例,needHeartbeat:true
                                  -->startHeatbeatTimer()//启动心跳计数器
                          -->ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap)/
                          -->Map<String, ReferenceCountExchangeClient> referenceClientMap.put("10.10.10.10:20880", 上边的ReferenceCountExchangeClient实例)
                      //3.2 创建DubboInvoker(nettyClient的持有者,真正发起netty调用的Invoker,做两件事:选择nettyClient + 处理单向/异步/同步调用模板)
                      -->new DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers)
                      -->DubboProtocol.Set<Invoker<?>> invokers.add(上边的DubboInvoker实例)
                    -->ProtocolFilterWrapper.buildInvokerChain(final Invoker<T> invoker, String key, String group)
                  -->new InvokerDelegete(Invoker<T> invoker, URL url, URL providerUrl)
                  //3.3 将创建出来的Invoker缓存起来
                  -->newUrlInvokerMap.put("dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-consumer&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=16001&register.ip=10.10.10.10&remote.timestamp=1510127991625&side=consumer&timestamp=1510128022123", 上边的InvokerDelegate实例)
                -->toMethodInvokers(newUrlInvokerMap)
                -->Map<String, List<Invoker<T>>> newMethodInvokerMap:{sayHello=[InvokerDelegete实例], *=[InvokerDelegete实例]}
      //4 将directory封装成一个ClusterInvoker(MockClusterInvoker)
      -->cluster.join(directory)
        -->Cluster$Adaptive.join(directory)
          -->ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("failover")//MockClusterWrapper包装FailoverCluster
            -->MockClusterWrapper.join(Directory<T> directory)
              -->FailoverCluster.join(Directory<T> directory)
                -->new FailoverClusterInvoker<T>(directory)
              -->MockClusterInvoker(Directory<T> directory, Invoker<T> invoker)//invoker:上边的FailoverClusterInvoker实例
  //二 获取代理
  -->JavassistProxyFactory.getProxy(Invoker<T> invoker, Class<?>[] interfaces)//invoker:上边的MockClusterInvoker实例, interfaces:[interface com.alibaba.dubbo.demo.DemoService, interface com.alibaba.dubbo.rpc.service.EchoService]
    -->Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker))
      -->Proxy.getProxy(ClassLoader cl, Class<?>... ics)//使用javassist获取一个动态类
      -->new InvokerInvocationHandler(invoker)//invoker:上边的MockClusterInvoker实例

消费者发布的时候总体做了两件事:创建 Invoker创建 API 接口的代理对象

2.1 创建Invoker

1. 获取注册中心:创建 ZkClient 实例,连接 zk
    //1 获取注册中心:创建ZkClient实例,连接zk
    -->Registry registry = registryFactory.getRegistry(url)
      -->AbstractRegistryFactory.getRegistry(URL url)
        -->ZookeeperRegistryFactory.createRegistry(URL url)
          -->new ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter)
            -->ZkclientZookeeperTransporter.connect(URL url)
              -->new ZkclientZookeeperClient(URL url)
                -->new ZkClient(url.getBackupAddress())
            -->AbstractRegistryFactory.Map<String, Registry> REGISTRIES.put("zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService", 上边的ZookeeperRegistry实例)

与provider相同,不再赘述。

2. 向注册中心注册 consumer 服务
      //2 向注册中心注册服务
      -->registry.register(url)
        -->ZookeeperRegistry.doRegister(URL url)
          -->AbstractZookeeperClient.create(String path, boolean ephemeral)

consumer 完成注册后,会在 zk 上创建节点(url 解码后):

/dubbo
- /com.alibaba.dubbo.demo.DemoService
-- /consumers
--- /consumer://10.10.10.10/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&side=consumer&timestamp=1510225913509
3. 使用 RegistryDirectory 订阅 providers、configurators、routers 节点

其中,configurators 节点用于覆盖配置(实现“热配置”),routers 节点用于配置路由信息。这里重点说一下 providers节点,该节点下存储着 DemoService 的服务提供者列表,当该列表发生变化时(添加 provider 机器或者宕机),会通知 consumer 进行 refreshInvoker 操作。看一下 RegistryDirectory 订阅 providers 的逻辑。

          //3.1 会获取当前节点下已经存在的字节点(第一次服务发现发生在这里),添加子节点变化监听器
          -->List<String> children = zkClient.addChildListener(path, zkListener)
          -->AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls)
            -->saveProperties(url)
            -->RegistryDirectory.notify(List<URL> urls)
              //仅仅针对的是providers
              -->refreshInvoker(List<URL> invokerUrls)
                -->toInvokers(List<URL> urls
                  -->ProtocolFilterWrapper.refer(Class<T> type, URL url)
                    -->DubboProtocol.refer(Class<T> serviceType, URL url)
                      //3.1.1 创建ExchangeClient,对第一次服务发现providers路径下的相关url建立长连接
                      -->getClients(URL url)
                        -->getSharedClient(URL url)
                          -->ExchangeClient exchangeClient = initClient(url)
                            -->Exchangers.connect(url, requestHandler)
                              -->HeaderExchanger.connect(URL url, ExchangeHandler handler)
                                -->new DecodeHandler(new HeaderExchangeHandler(handler)))
                                  -->Transporters.connect(URL url, ChannelHandler... handlers)
                                    -->NettyTransporter.connect(URL url, ChannelHandler listener)
                                      -->new NettyClient(url, listener)
                                        -->new MultiMessageHandler(HeartbeatHandler(AllChannelHandler(handler)))
                                        -->getChannelCodec(url)//获取Codec2,这里是DubboCountCodec实例
                                        -->doOpen()//开启netty客户端
                                        -->doConnect()//连接服务端,建立长连接
                                -->new HeaderExchangeClient(Client client, boolean needHeartbeat)//上述的NettyClient实例,needHeartbeat:true
                                  -->startHeatbeatTimer()//启动心跳计数器
                          -->ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap)/
                          -->Map<String, ReferenceCountExchangeClient> referenceClientMap.put("10.10.10.10:20880", 上边的ReferenceCountExchangeClient实例)
                      //3.2 创建DubboInvoker
                      -->new DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers)
                      -->DubboProtocol.Set<Invoker<?>> invokers.add(上边的DubboInvoker实例)
                    -->ProtocolFilterWrapper.buildInvokerChain(final Invoker<T> invoker, String key, String group)
                  -->new InvokerDelegete(Invoker<T> invoker, URL url, URL providerUrl)
                  //3.3 将创建出来的Invoker缓存起来
                  -->newUrlInvokerMap.put("dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-consumer&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=16001&register.ip=10.10.10.10&remote.timestamp=1510127991625&side=consumer&timestamp=1510128022123", 上边的InvokerDelegate实例)
                -->toMethodInvokers(newUrlInvokerMap)
                -->Map<String, List<Invoker<T>>> newMethodInvokerMap:{sayHello=[InvokerDelegete实例], *=[InvokerDelegete实例]}
总体流程 :
  1. 首先添加 provider 子节点监听器,同时进行 第一次服务发现,找出当前注册在 zk 上的 provider 节点。
  2. 然后创建 ReferenceCountExchangeClient,为每一个 provider 创建一条 Netty 长连接(开启了 Netty 客户端并连接 provider 的 Netty 服务端)
  3. 开启心跳定时器
  4. 缓存 ReferenceCountExchangeClient
  5. 将创建出来的 ReferenceCountExchangeClient 封装到 DubboInvoker 实例中
  6. 根据条件获取相关的 filter,之后使用这些 fiter 对 DubboInvoker 实例进行链式包装,将包装后的 DubboInvoker 封装到 InvokerDelegete 中
  7. 最后将该 InvokerDelegete 实例按照方法名封装到 Map<String, List<Invoker<T>>> newMethodInvokerMap 中。(该 map 也是 consumer 调用 provider 时,获取 provider 实例的地方,该 map 是 RegistryDirectory 的一个属性,所以我们可以将 RegistryDirectory 看做是一个 provider 的客户端缓存器

Directory.List<Router> routers 会在两个地方被设置

  • 初次创建 Directory 实例时,在 Directory 构造器中进行设置;
  • notify 的时候会进行 routers 的重新设置。

关于 Directory 的设计,后续分析

4. 将 directory 封装成一个 ClusterInvoker(MockClusterInvoker)
      //4 将directory封装成一个ClusterInvoker(MockClusterInvoker)
      -->cluster.join(directory)
        -->Cluster$Adaptive.join(directory)
          -->ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("failover")//MockClusterWrapper包装FailoverCluster
            -->MockClusterWrapper.join(Directory<T> directory)
              -->FailoverCluster.join(Directory<T> directory)
                -->new FailoverClusterInvoker<T>(directory)
              -->MockClusterInvoker(Directory<T> directory, Invoker<T> invoker)//invoker:上边的FailoverClusterInvoker实例

Dubbo 实现了集群容错功能。在 consumer 发布的最后流程中,实现了集群容错。

  1. 首先根据 Dubbo SPI 机制获取指定类型的 Cluster 实现,这里默认是 FailoverCluster(失败重试机制);
  2. 之后将上边的 RegistryDirectory 封装到 FailoverClusterInvoker 实例中;
  3. 最后创建一个 MockClusterInvoker 实例,封装了 RegistryDirectoryFailoverClusterInvoker。(MockClusterInvoker 用于实现服务降级功能)

到这里,consumer 创建 Invoker 的源码就结束了。总结一下

  1. 获取注册中心:创建 ZkClient 实例,连接 zk
  2. 向注册中心注册 consumer 服务
  3. 使用 RegistryDirectory 订阅 providers、configurators、routers 节点

3.1. 添加 provider 子节点监听器,同时进行第一次服务发现,找出当前注册在 zk 上的 provider 节点

3.1.1. 创建 ReferenceCountExchangeClient,为每一个 provider 创建一条 Netty 长连接(开启了 Netty 客户端并连接 provider 的 Netty 服务端)
3.1.2. 开启心跳定时器
3.2. 根据条件获取相关的 filter,之后使用这些 fiter 对 DubboInvoker 实例进行链式包装,将包装后的 DubboInvoker 封装到 InvokerDelegete 中
3.3. 最后将该 InvokerDelegete 实例按照方法名封装到 Map<String, List<Invoker<T>>> newMethodInvokerMap

  1. 将 directory 封装成一个 ClusterInvoker(MockClusterInvoker)

4.1 首先根据 Dubbo SPI 机制获取指定类型的 Cluster 实现,这里默认是 FailoverCluster(失败重试机制);
4.2 之后将上边的 RegistryDirectory 封装到 FailoverClusterInvoker 实例中;
4.3 最后创建一个 MockClusterInvoker 实例,封装了 RegistryDirectory 和 FailoverClusterInvoker。(MockClusterInvoker用于实现服务降级功能)

我们可以看到,最终获取到的 Invoker 是 MockClusterInvoker 实例

关于 Cluster 的设计,后续分析

2.2 创建API接口的代理对象

  //二 获取代理
  -->JavassistProxyFactory.getProxy(Invoker<T> invoker, Class<?>[] interfaces)//invoker:上边的MockClusterInvoker实例, interfaces:[interface com.alibaba.dubbo.demo.DemoService, interface com.alibaba.dubbo.rpc.service.EchoService]
    -->Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker))
      -->Proxy.getProxy(ClassLoader cl, Class<?>... ics)//使用javassist获取一个动态类
      -->new InvokerInvocationHandler(invoker)//invoker:上边的MockClusterInvoker实例

获取 API 接口代理的逻辑比较简单,注意这里的 Proxy 是 com.alibaba.dubbo.common.bytecode.Proxy,而非 JDK 的 Proxy。
这里首先调用 Proxy.getProxy(interfaces) 获取到一个创建代理的工厂类 com.alibaba.dubbo.common.bytecode.Proxy0,如下:

package com.alibaba.dubbo.common.bytecode;

import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.common.bytecode.Proxy;
import com.alibaba.dubbo.common.bytecode.proxy0;
import java.lang.reflect.InvocationHandler;

public class Proxy0 extends Proxy implements ClassGenerator.DC {
    public Object newInstance(InvocationHandler invocationHandler) {
        return new proxy0(invocationHandler);
    }
}

之后调用了 Proxy0#newInstance 方法,创建了一个 com.alibaba.dubbo.common.bytecode.proxy0 实例,该实例就是最终的 DemoService 的代理对象。

DemoService demoService = (DemoService) context.getBean("demoService");

这里的 demoService 就是上述的 com.alibaba.dubbo.common.bytecode.proxy0 实例。

package com.alibaba.dubbo.common.bytecode;

import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.demo.DemoService;
import com.alibaba.dubbo.rpc.service.EchoService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class proxy0 implements EchoService, DemoService {
    public static Method[] methods;
    private InvocationHandler handler;

    public String sayHello(String string) {
        Object[] arrobject = new Object[]{string};
        Object object = this.handler.invoke(this, methods[0], arrobject);
        return (String)object;
    }

    public Object $echo(Object object) {
        Object[] arrobject = new Object[]{object};
        Object object2 = this.handler.invoke(this, methods[1], arrobject);
        return object2;
    }

    public proxy0() {
    }

    public proxy0(InvocationHandler invocationHandler) {
        this.handler = invocationHandler;
    }
}

可以看到,当调用 proxy0#sayHello 时,实际上其内部执行的是 InvocationHandler#invoke,来看一下 InvocationHandler。

public class InvokerInvocationHandler implements InvocationHandler {
    private final Invoker<?> invoker; //上边的MockClusterInvoker实例

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        ...
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }
}

InvocationHandler#invoke 调用的又是 MockClusterInvoker#invoke 方法。到此为止,整个服务引用的源码分析就完成了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容