← 返回文章列表

Dubbo 底层原理系列(四):服务引用

2020-08-03·5 分钟阅读

前言

服务引用是 Dubbo 消费端启动的核心流程,从注册中心订阅服务并创建代理对象。理解服务引用原理有助于排查服务调用问题。

技术亮点

技术点难度面试价值本文覆盖
服务引用流程⭐⭐⭐⭐高频考点
代理创建⭐⭐⭐⭐进阶考点
Directory⭐⭐⭐⭐⭐进阶考点
Cluster Invoker⭐⭐⭐⭐⭐高频考点

面试考点

  1. Dubbo 服务引用的整体流程是怎样的?
  2. 代理对象是如何创建的?
  3. Directory 的作用是什么?
  4. Cluster Invoker 是如何构建的?

服务引用入口

配置解析

┌─────────────────────────────────────────────────────────────────────────┐
│                        服务引用配置                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Spring XML 配置:                                                      │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  <dubbo:reference                                              │   │
│  │      id="userService"                                          │   │
│  │      interface="com.example.UserService"                       │   │
│  │      version="1.0.0"                                           │   │
│  │      group="production"                                        │   │
│  │      timeout="3000"                                            │   │
│  │      retries="2"                                               │   │
│  │      check="false" />                                          │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  注解方式:                                                             │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  @DubboReference(                                              │   │
│  │      version = "1.0.0",                                        │   │
│  │      group = "production",                                     │   │
│  │      timeout = 3000                                            │   │
│  │  )                                                             │   │
│  │  private UserService userService;                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

入口类 ReferenceBean

┌─────────────────────────────────────────────────────────────────────────┐
│                        ReferenceBean 入口                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  ReferenceBean 继承关系:                                        │   │
│  │                                                                 │   │
│  │  ReferenceBean<T>                                               │   │
│  │      extends ReferenceConfig<T>                                 │   │
│  │      implements FactoryBean<T>,                                 │   │
│  │                 InitializingBean                                │   │
│  │                                                                 │   │
│  │  核心方法:                                                     │   │
│  │                                                                 │   │
│  │  1. getObject() - FactoryBean 接口,返回代理对象                 │   │
│  │  2. afterPropertiesSet() - 初始化                               │   │
│  │  3. get() - 创建代理对象入口                                    │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  触发时机:                                                             │
│  • Spring 容器注入 Bean 时调用 getObject()                              │
│  • 第一次调用时懒加载(check=false)                                     │
│  • 启动时立即检查(check=true)                                         │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

服务引用流程

整体流程图

┌─────────────────────────────────────────────────────────────────────────┐
│                        服务引用整体流程                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ReferenceConfig.get()                                                  │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  1. 前置检查                                                     │   │
│  │         │                                                       │   │
│  │         ├──► 检查是否已创建                                      │   │
│  │         ├──► 检查配置是否完整                                    │   │
│  │         └──► 组装 URL                                            │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  2. 本地引用检查                                                 │   │
│  │         │                                                       │   │
│  │         └──► 检查是否有本地实现                                  │   │
│  │                 │                                               │   │
│  │                 └──► 有:创建本地 Invoker                        │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  3. 远程引用                                                     │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     createProxy(map)                                            │   │
│  │         │                                                       │   │
│  │         ├──► 直连模式:直接创建 Invoker                          │   │
│  │         │                                                       │   │
│  │         └──► 注册中心模式:                                      │   │
│  │                 │                                               │   │
│  │                 ├──► 订阅注册中心                                │   │
│  │                 ├──► 创建 Directory                              │   │
│  │                 └──► 创建 Cluster Invoker                        │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  4. 创建代理对象                                                 │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     proxy = proxyFactory.getProxy(invoker)                      │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│     返回代理对象                                                        │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

createProxy 流程

┌─────────────────────────────────────────────────────────────────────────┐
│                        createProxy 详细流程                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  createProxy(Map<String, String> map)                                   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  1. 判断引用范围                                                 │   │
│  │         │                                                       │   │
│  │         ├──► scope=local:本地引用                               │   │
│  │         │                                                       │   │
│  │         ├──► scope=remote:远程引用                              │   │
│  │         │                                                       │   │
│  │         └──► 默认:先检查本地,再远程                             │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  2. 直连模式判断                                                 │   │
│  │         │                                                       │   │
│  │         └──► URL 包含 ip:port → 直连                            │   │
│  │                   │                                             │   │
│  │                   └──► invoker = protocol.refer(url)            │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  3. 注册中心模式                                                 │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     List<URL> us = loadRegistries();                            │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     for (URL url : us) {                                        │   │
│  │         invoker = protocol.refer(url);  // RegistryProtocol     │   │
│  │     }                                                           │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  4. 创建 Cluster Invoker                                         │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     if (invokers.size() == 1) {                                 │   │
│  │         return invokers.get(0);                                 │   │
│  │     }                                                           │   │
│  │     return cluster.join(directory);                             │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  5. 创建代理                                                     │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     return (T) proxyFactory.getProxy(invoker);                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Directory 服务目录

Directory 架构

┌─────────────────────────────────────────────────────────────────────────┐
│                        Directory 架构                                    │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  Directory 接口:维护服务提供者列表                               │   │
│  │                                                                 │   │
│  │  public interface Directory<T> extends Node {                  │   │
│  │      List<Invoker<T>> list(Invocation invocation);             │   │
│  │      Class<T> getInterface();                                   │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  实现类:                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  RegistryDirectory                                              │   │
│  │  ┌───────────────────────────────────────────────────────────┐ │   │
│  │  │                                                           │ │   │
│  │  │  • 从注册中心订阅服务提供者列表                              │ │   │
│  │  │  • 监听注册中心变化,动态更新 Invoker 列表                  │ │   │
│  │  │  • 实现 NotifyListener 接口                                 │ │   │
│  │  │                                                           │ │   │
│  │  │  核心属性:                                                 │ │   │
│  │  │  • Map<String, Invoker<T>> urlInvokerMap                  │ │   │
│  │  │  • Map<String, List<Invoker<T>>> methodInvokerMap         │ │   │
│  │  │                                                           │ │   │
│  │  └───────────────────────────────────────────────────────────┘ │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

服务订阅流程

┌─────────────────────────────────────────────────────────────────────────┐
│                        服务订阅流程                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  RegistryProtocol.refer()                                               │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  1. 获取注册中心实例                                             │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     Registry registry = registryFactory.getRegistry(url);       │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  2. 创建 Directory                                               │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     RegistryDirectory<T> directory = new RegistryDirectory<>(); │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  3. 订阅服务                                                     │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     registry.subscribe(url, directory);                         │   │
│  │         │                                                       │   │
│  │         └──► ZookeeperRegistry.subscribe()                      │   │
│  │                   │                                             │   │
│  │                   ├──► 创建监听节点                              │   │
│  │                   ├──► 获取 providers 列表                       │   │
│  │                   └──► 触发 notify()                             │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  4. 构建 Cluster Invoker                                         │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     ClusterInvoker<T> clusterInvoker = cluster.join(directory);│   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│     返回 clusterInvoker                                                 │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

服务变更通知

┌─────────────────────────────────────────────────────────────────────────┐
│                        服务变更通知流程                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  Zookeeper 节点变化                                                     │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  RegistryDirectory.notify(List<URL> urls)                       │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │  1. 解析 URL 分类                                                │   │
│  │         │                                                       │   │
│  │         ├──► providers:服务提供者                               │   │
│  │         ├──► routers:路由规则                                   │   │
│  │         └──► configurators:动态配置                             │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  2. 更新 Invoker 列表                                            │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     refreshInvoker(urls)                                        │   │
│  │         │                                                       │   │
│  │         ├──► 销毁无效 Invoker                                    │   │
│  │         └──► 创建新 Invoker                                      │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│     Invoker 列表更新完成                                                │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Cluster Invoker

Cluster 架构

┌─────────────────────────────────────────────────────────────────────────┐
│                        Cluster 架构                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  Cluster 接口:将多个 Invoker 合并成一个                          │   │
│  │                                                                 │   │
│  │  @SPI("failover")                                               │   │
│  │  public interface Cluster {                                     │   │
│  │      @Adaptive                                                  │   │
│  │      <T> Invoker<T> join(Directory<T> directory);               │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  Cluster Invoker 继承关系:                                             │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  AbstractClusterInvoker                                        │   │
│  │      │                                                          │   │
│  │      ├──► FailoverClusterInvoker  (失败重试)                    │   │
│  │      ├──► FailfastClusterInvoker  (快速失败)                    │   │
│  │      ├──► FailsafeClusterInvoker  (失败安全)                    │   │
│  │      ├──► FailbackClusterInvoker  (失败自动恢复)                │   │
│  │      ├──► ForkingClusterInvoker   (并行调用)                    │   │
│  │      └──► BroadcastClusterInvoker (广播调用)                    │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

AbstractClusterInvoker 核心方法

┌─────────────────────────────────────────────────────────────────────────┐
│                        AbstractClusterInvoker 核心逻辑                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  invoke(Invocation invocation)                                          │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  1. 获取 Invoker 列表                                            │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     List<Invoker<T>> invokers = directory.list(invocation);    │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  2. 路由过滤                                                     │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     for (Router router : routers) {                            │   │
│  │         invokers = router.route(invokers);                     │   │
│  │     }                                                           │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  3. 负载均衡选择                                                 │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     Invoker<T> invoker = select(loadbalance, invocation,       │   │
│  │                                 invokers, selected);            │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  4. 执行调用                                                     │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     doInvoke(invocation, invokers, loadbalance)                 │   │
│  │         │                                                       │   │
│  │         └──► 子类实现具体容错逻辑                                │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│         │                                                               │
│         ▼                                                               │
│     返回调用结果                                                        │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

代理对象创建

ProxyFactory.getProxy

┌─────────────────────────────────────────────────────────────────────────┐
│                        代理对象创建                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  proxyFactory.getProxy(invoker)                                 │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │  JavassistProxyFactory.getProxy()                               │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │  1. 生成代理类代码                                               │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     代码模板:                                                   │   │
│  │     public class proxy0 implements UserService {                │   │
│  │         private Invoker invoker;                                │   │
│  │                                                                 │   │
│  │         public User getUser(Long id) {                          │   │
│  │             Object[] args = new Object[]{id};                   │   │
│  │             Invocation inv = new RpcInvocation(                 │   │
│  │                 "getUser", args, ...);                          │   │
│  │             return (User) invoker.invoke(inv);                  │   │
│  │         }                                                       │   │
│  │     }                                                           │   │
│  │                                                                 │   │
│  │  2. 编译并实例化                                                 │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │     Class<?> clazz = compiler.compile(code);                    │   │
│  │     Object proxy = clazz.newInstance();                         │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  代理对象特点:                                                         │
│  • 实现服务接口                                                        │
│  • 持有 Invoker 引用                                                   │
│  • 方法调用转为 Invoker.invoke()                                       │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

总结

本文介绍了 Dubbo 服务引用原理:

概念说明
入口ReferenceBean.getObject()
Directory维护服务提供者列表
Cluster合并多个 Invoker
代理将方法调用转为 Invoker.invoke()

参考资料

下一章预告

下一章将深入解析 注册中心原理,包括:

  • 注册中心接口设计
  • Zookeeper 注册中心实现
  • 订阅与通知机制
分享: