← 返回文章列表

Dubbo 底层原理系列(五):注册中心

2020-08-16·5 分钟阅读

前言

注册中心是 Dubbo 实现服务发现的核心组件,负责服务的注册、订阅和变更通知。理解注册中心原理对于排查服务发现问题至关重要。

技术亮点

技术点难度面试价值本文覆盖
Registry 接口⭐⭐⭐高频考点
Zookeeper 实现⭐⭐⭐⭐高频考点
订阅通知机制⭐⭐⭐⭐进阶考点
缓存机制⭐⭐⭐进阶考点

面试考点

  1. Dubbo 支持哪些注册中心?
  2. Zookeeper 注册中心是如何实现的?
  3. 服务订阅和变更通知是如何工作的?
  4. 注册中心缓存的作用是什么?

注册中心接口

Registry 接口设计

┌─────────────────────────────────────────────────────────────────────────┐
│                        Registry 接口设计                                 │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  RegistryService 接口:                                         │   │
│  │  ┌───────────────────────────────────────────────────────────┐ │   │
│  │  │                                                           │ │   │
│  │  │  public interface RegistryService {                       │ │   │
│  │  │                                                           │ │   │
│  │  │      // 注册服务                                           │ │   │
│  │  │      void register(URL url);                              │ │   │
│  │  │                                                           │ │   │
│  │  │      // 取消注册                                           │ │   │
│  │  │      void unregister(URL url);                            │ │   │
│  │  │                                                           │ │   │
│  │  │      // 订阅服务                                           │ │   │
│  │  │      void subscribe(URL url, NotifyListener listener);    │ │   │
│  │  │                                                           │ │   │
│  │  │      // 取消订阅                                           │ │   │
│  │  │      void unsubscribe(URL url, NotifyListener listener);  │ │   │
│  │  │                                                           │ │   │
│  │  │      // 查询服务列表                                        │ │   │
│  │  │      List<URL> lookup(URL url);                           │ │   │
│  │  │  }                                                        │ │   │
│  │  │                                                           │ │   │
│  │  └───────────────────────────────────────────────────────────┘ │   │
│  │                                                                 │   │
│  │  Registry 接口(继承 Node):                                   │   │
│  │  ┌───────────────────────────────────────────────────────────┐ │   │
│  │  │                                                           │ │   │
│  │  │  public interface Registry extends RegistryService, Node {│ │   │
│  │  │      URL getUrl();  // 获取注册中心 URL                    │ │   │
│  │  │      boolean isAvailable();  // 是否可用                   │ │   │
│  │  │      void destroy();  // 销毁                              │ │   │
│  │  │  }                                                        │ │   │
│  │  │                                                           │ │   │
│  │  └───────────────────────────────────────────────────────────┘ │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

RegistryFactory 接口

┌─────────────────────────────────────────────────────────────────────────┐
│                        RegistryFactory 接口                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  @SPI("dubbo")                                                  │   │
│  │  public interface RegistryFactory {                            │   │
│  │                                                                 │   │
│  │      @Adaptive({"protocol"})                                   │   │
│  │      Registry getRegistry(URL url);                            │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  │  实现类:                                                       │   │
│  │  • ZookeeperRegistryFactory                                    │   │
│  │  • NacosRegistryFactory                                        │   │
│  │  • RedisRegistryFactory                                        │   │
│  │  • MulticastRegistryFactory                                    │   │
│  │  • SimpleRegistryFactory (Simple/Direct)                       │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  支持的注册中心:                                                       │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  注册中心      配置示例                          特点            │   │
│  │  ─────────────────────────────────────────────────────────────  │   │
│  │  Zookeeper    zookeeper://127.0.0.1:2181     推荐,生产可用     │   │
│  │  Nacos        nacos://127.0.0.1:8848         阿里云,功能丰富   │   │
│  │  Redis        redis://127.0.0.1:6379         简单,性能好       │   │
│  │  Multicast    multicast://224.5.6.7:1234     无需中心节点       │   │
│  │  Simple       dubbo://127.0.0.1:9090         点对点直连         │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Zookeeper 注册中心

架构设计

┌─────────────────────────────────────────────────────────────────────────┐
│                        Zookeeper 注册中心架构                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  ZookeeperRegistry                                             │   │
│  │  ┌───────────────────────────────────────────────────────────┐ │   │
│  │  │                                                           │ │   │
│  │  │  继承:FailbackRegistry(失败重试)                         │ │   │
│  │  │                                                           │ │   │
│  │  │  核心属性:                                                 │ │   │
│  │  │  • ZookeeperClient zkClient  // ZK 客户端                  │ │   │
│  │  │  • Set<String> anyServices  // 所有服务                    │ │   │
│  │  │  • ConcurrentMap<URL, NotifyListener> listeners            │ │   │
│  │  │                                                           │ │   │
│  │  │  核心方法:                                                 │ │   │
│  │  │  • doRegister(URL url)     // 注册实现                     │ │   │
│  │  │  • doUnregister(URL url)   // 取消注册实现                 │ │   │
│  │  │  • doSubscribe(URL url, NotifyListener listener)          │ │   │
│  │  │  • doUnsubscribe(URL url, NotifyListener listener)        │ │   │
│  │  │                                                           │ │   │
│  │  └───────────────────────────────────────────────────────────┘ │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

节点结构

┌─────────────────────────────────────────────────────────────────────────┐
│                        Zookeeper 节点结构                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  默认根节点:/dubbo                                                     │
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  /dubbo                                                         │   │
│  │      │                                                          │   │
│  │      └── com.example.UserService  (服务接口)                     │   │
│  │              │                                                  │   │
│  │              ├── providers  (服务提供者)                         │   │
│  │              │       │                                          │   │
│  │              │       ├── dubbo://192.168.1.1:20880/...         │   │
│  │              │       └── dubbo://192.168.1.2:20880/...         │   │
│  │              │                                                  │   │
│  │              ├── consumers  (服务消费者)                         │   │
│  │              │       │                                          │   │
│  │              │       └── consumer://192.168.1.100/...          │   │
│  │              │                                                  │   │
│  │              ├── routers  (路由规则)                             │   │
│  │              │       │                                          │   │
│  │              │       └── condition://...                        │   │
│  │              │                                                  │   │
│  │              └── configurators  (动态配置)                       │   │
│  │                      │                                          │   │
│  │                      └── override://...                         │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  节点类型:                                                             │
│  • providers:临时节点,服务下线自动删除                                 │
│  • consumers:临时节点,消费者下线自动删除                               │
│  • routers:持久节点,存储路由规则                                       │
│  • configurators:持久节点,存储动态配置                                 │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

注册实现

┌─────────────────────────────────────────────────────────────────────────┐
│                        doRegister 实现                                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  protected void doRegister(URL url) {                          │   │
│  │      try {                                                      │   │
│  │          // 构建节点路径                                         │   │
│  │          String path = toUrlPath(url);                          │   │
│  │          // 例如:/dubbo/com.example.UserService/providers/     │   │
│  │          //       dubbo://192.168.1.1:20880/...                 │   │
│  │                                                                 │   │
│  │          // 创建临时节点                                         │   │
│  │          zkClient.create(path, true);                           │   │
│  │          // true 表示临时节点                                    │   │
│  │                                                                 │   │
│  │      } catch (Exception e) {                                    │   │
│  │          throw new RpcException("Failed to register " + url);  │   │
│  │      }                                                          │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  注册流程:                                                             │
│  1. 构建节点路径                                                        │
│  2. 创建父节点(持久节点)                                               │
│  3. 创建服务节点(临时节点)                                             │
│  4. 会话断开后自动删除临时节点                                           │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

订阅与通知机制

订阅实现

┌─────────────────────────────────────────────────────────────────────────┐
│                        doSubscribe 实现                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  protected void doSubscribe(URL url, NotifyListener listener) { │   │
│  │                                                                 │   │
│  │      // 1. 构建监听路径                                          │   │
│  │      String path = toCategoryPath(url);                        │   │
│  │      // 例如:/dubbo/com.example.UserService/providers          │   │
│  │                                                                 │   │
│  │      // 2. 添加子节点监听器                                      │   │
│  │      List<String> children = zkClient.addChildListener(        │   │
│  │          path,                                                  │   │
│  │          new ChildListener() {                                  │   │
│  │              public void childChanged(String path,             │   │
│  │                                         List<String> children) {│   │
│  │                  // 子节点变化时触发通知                         │   │
│  │                  notify(url, listener, toUrls(children));      │   │
│  │              }                                                  │   │
│  │          }                                                      │   │
│  │      );                                                         │   │
│  │                                                                 │   │
│  │      // 3. 首次获取并通知                                        │   │
│  │      notify(url, listener, toUrls(children));                  │   │
│  │  }                                                              │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  订阅流程:                                                             │
│  1. 构建监听路径                                                        │
│  2. 注册子节点监听器                                                    │
│  3. 首次获取子节点列表并通知                                             │
│  4. 后续变化通过监听器触发通知                                           │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

通知机制

┌─────────────────────────────────────────────────────────────────────────┐
│                        通知机制                                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  Zookeeper 节点变化                                              │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │  ChildListener.childChanged()                                   │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │  AbstractRegistry.notify(url, listener, urls)                   │   │
│  │         │                                                       │   │
│  │         ▼                                                       │   │
│  │  ┌─────────────────────────────────────────────────────────┐   │   │
│  │  │  1. 保存到本地缓存                                        │   │   │
│  │  │         │                                                │   │   │
│  │  │         ▼                                                │   │   │
│  │  │     saveProperties(url, urls)                            │   │   │
│  │  │         │                                                │   │   │
│  │  │         └──► 写入本地文件(断网恢复用)                     │   │   │
│  │  │                                                          │   │   │
│  │  │  2. 触发监听器回调                                        │   │   │
│  │  │         │                                                │   │   │
│  │  │         ▼                                                │   │   │
│  │  │     listener.notify(urls)                                │   │   │
│  │  │         │                                                │   │   │
│  │  │         └──► RegistryDirectory.notify()                   │   │   │
│  │  │                   │                                      │   │   │
│  │  │                   └──► 刷新 Invoker 列表                   │   │   │
│  │  └─────────────────────────────────────────────────────────┘   │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

缓存机制

本地缓存

┌─────────────────────────────────────────────────────────────────────────┐
│                        本地缓存机制                                      │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  缓存文件路径:                                                  │   │
│  │  ${user.home}/.dubbo/dubbo-registry-${host}-cache.cache        │   │
│  │                                                                 │   │
│  │  缓存作用:                                                     │   │
│  │  • 注册中心宕机时,从本地缓存恢复服务列表                         │   │
│  │  • 消费者启动时,优先使用本地缓存                                │   │
│  │  • 提供服务降级能力                                             │   │
│  │                                                                 │   │
│  │  缓存更新:                                                     │   │
│  │  • 每次收到注册中心通知时更新                                    │   │
│  │  • 异步写入文件                                                 │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  配置项:                                                               │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  # 是否使用本地缓存                                              │   │
│  │  dubbo.registry.file-cache=true                                 │   │
│  │                                                                 │   │
│  │  # 缓存文件路径                                                  │   │
│  │  dubbo.registry.cache-file=/path/to/cache                      │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

FailbackRegistry 失败重试

┌─────────────────────────────────────────────────────────────────────────┐
│                        FailbackRegistry 失败重试                         │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                                                                 │   │
│  │  失败重试机制:                                                  │   │
│  │                                                                 │   │
│  │  • 注册失败 → 放入 failedRegistered 集合                        │   │
│  │  • 订阅失败 → 放入 failedSubscribed 集合                        │   │
│  │  • 定时任务重试失败操作                                          │   │
│  │                                                                 │   │
│  │  重试定时器:                                                    │   │
│  │  ┌───────────────────────────────────────────────────────────┐ │   │
│  │  │                                                           │ │   │
│  │  │  // 默认 5 秒重试一次                                       │ │   │
│  │  │  private static final int DEFAULT_RETRY_PERIOD = 5 * 1000;│ │   │
│  │  │                                                           │ │   │
│  │  │  ScheduledExecutorService retryExecutor = ...             │ │   │
│  │  │                                                           │ │   │
│  │  │  retryExecutor.scheduleAtFixedRate(() -> {                │ │   │
│  │  │      // 重试注册                                           │ │   │
│  │  │      retryRegister();                                     │ │   │
│  │  │      // 重试订阅                                           │ │   │
│  │  │      retrySubscribe();                                    │ │   │
│  │  │  }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);     │ │   │
│  │  │                                                           │ │   │
│  │  └───────────────────────────────────────────────────────────┘ │   │
│  │                                                                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

总结

本文介绍了 Dubbo 注册中心原理:

概念说明
RegistryService注册、订阅、查询接口
ZookeeperRegistry基于 ZK 的注册中心实现
订阅机制子节点监听 + 通知回调
本地缓存断网恢复、服务降级

参考资料

下一章预告

下一章将深入解析 负载均衡原理,包括:

  • 负载均衡策略
  • Random、RoundRobin、LeastActive 实现
  • 一致性 Hash
分享: