myrpc 学习笔记-005 注册中心

项目地址 欢迎访问

https://gitee.com/longlong5/myrpc

笔记总览 myrpc 学习笔记-001 实现简易版 rpc myrpc 学习笔记-002 配置加载 myrpc 学习笔记-003 Mock 服务代理 myrpc 学习笔记-004 序列化实现和 SPI 机制 myrpc 学习笔记-005 注册中心 myrpc 学习笔记-006 自定义协议 myrpc 学习笔记-007 负载均衡 myrpc 学习笔记-008 重试机制 myrpc 学习笔记-009 容错机制 myrpc 学习笔记-0010 启动机制和注解驱动

架构图v5.0.0

  • 实现注册中心(etcd zookeeper)
  • 支持自定义注册中心实现类

v5.0.0.png

一、注册中心是什么?

注册中心是RPC框架的“通讯录”

  • 服务端(提供者):启动时把自己的地址、接口信息注册进去
  • 客户端(消费者):调用时从注册中心发现可用服务节点
  • 心跳/监听:保证节点健康,自动剔除故障节点

二、核心功能

  1. 服务注册:服务启动时把节点信息存入注册中心
  2. 服务发现:客户端获取可用服务节点列表
  3. 心跳续约:防止节点因故障假死
  4. 服务下线:服务停机主动/自动删除节点
  5. 监听机制:节点变化实时通知客户端
  6. 本地缓存:提升性能,减轻注册中心压力
  7. 多注册中心支持:Etcd / ZooKeeper 可插拔

三、整体结构设计

1. 统一接口(规范)

java
复制代码
public interface Registry { void init(RegistryConfig config); // 初始化 void register(ServiceMetaInfo info); // 注册 void unRegister(ServiceMetaInfo info); // 注销 List<ServiceMetaInfo> serviceDiscovery(String serviceKey); // 发现 void destroy(); // 销毁 void heartBeat(); // 心跳 void watch(String key); // 监听 }

2. 工厂 + SPI 可扩展

java
复制代码
public class RegistryFactory { static { SpiLoader.load(Registry.class); } public static Registry getRegistry(String key) { return SpiLoader.getInstance(Registry.class, key); } }

支持 Etcd、ZooKeeper、Nacos 无缝切换

3. 本地注册(内存存储)

java
复制代码
public class LocalRegistry { private static final Map<String, Class<?>> SERVICE_MAP = new ConcurrentHashMap<>(); }

作用:服务端反射调用时快速找到实现类。

四、Etcd 注册中心实现

1. 核心机制

  • Key-Value 存储
  • 租约(Lease):节点30秒过期
  • 心跳续约:每10秒续期一次
  • Watch 监听:节点删除自动清空缓存

2. 存储结构

text
复制代码
/rpc/服务名:版本号/地址:端口 -> {服务元信息}

示例:

text
复制代码
/rpc/com.xxx.UserService:1.0/127.0.0.1:8080

3. 服务注册流程

  1. 申请 30s租约
  2. 拼接 key、存储 value
  3. 绑定租约存入 Etcd
  4. 加入本地缓存
  5. 启动心跳定时任务

4. 心跳实现(最关键)

java
复制代码
// 每10秒执行一次 CronUtil.schedule("*/10 * * * * *", task -> { for (String key : localRegisterNodeKeySet) { // 获取租约ID long leaseId = kvs.get(0).getLease(); // 手动续约 leaseClient.keepAliveOnce(leaseId); } });

5. 服务发现流程

  1. 先查本地缓存
  2. 缓存不存在 → Etcd前缀查询
  3. 解析结果 → 写入本地缓存
  4. 对每个节点 开启监听

6. 监听机制

监听节点删除事件:

java
复制代码
watchClient.watch(key, response -> { for (WatchEvent event : response.getEvents()) { if (event.getEventType() == DELETE) { // 清空缓存,下次强制拉新 registryServiceCache.clearCache(); } } });

五、ZooKeeper 注册中心实现

1. 核心机制

  • 临时节点:会话断开自动删除
  • 天然心跳:无需手动续约
  • Cache 监听:节点变化自动感知

2. 为什么不用心跳?

因为 ZooKeeper 临时节点特性

  • 客户端断开 → 节点自动删除
  • 无需代码实现心跳

3. 服务注册

java
复制代码
serviceDiscovery.registerService(instance);

4. 服务监听

java
复制代码
CuratorCache cache = CuratorCache.build(client, key); cache.listenable().addListener( CuratorCacheListener.builder() .forDeletes(child -> clearCache()) .build() );

六、服务本地缓存(RegistryServiceCache)

java
复制代码
public class RegistryServiceCache { List<ServiceMetaInfo> serviceCache; void writeCache(); List<ServiceMetaInfo> readCache(); void clearCache(); }

作用:

  • 避免频繁请求注册中心
  • 提高调用速度
  • 注册中心宕机仍可使用缓存调用

七、服务启动初始化(ProviderBootstrap)

java
复制代码
public class ProviderBootstrap { public static void init(List<ServiceRegisterInfo<?>> list) { // 1. RPC 初始化 RpcApplication.init(); // 2. 本地注册 LocalRegistry.register(serviceName, implClass); // 3. 注册中心注册 Registry registry = RegistryFactory.getRegistry(type); registry.register(serviceMetaInfo); // 4. 启动 TCP 服务 VertxTcpServer server = new VertxTcpServer(); server.doStart(port); } }

八、客户端调用流程(ServiceProxy)

java
复制代码
// 1. 从注册中心发现服务 List<ServiceMetaInfo> list = registry.serviceDiscovery(serviceKey); // 2. 负载均衡选择一个节点 ServiceMetaInfo selected = loadBalancer.select(params, list); // 3. 发送 TCP 请求 RpcResponse response = VertxTcpClient.doRequest(request, info);

九、总结

  1. 接口化设计:可随意切换注册中心实现
  2. SPI 加载:高度可扩展
  3. Etcd 租约+心跳:保证节点健康
  4. ZooKeeper 临时节点:天然健康检查
  5. 本地缓存:高性能
  6. Watch 监听:实时感知节点上下线
  7. 优雅停机:服务下线主动删除节点
  8. 统一存储结构:服务名+版本+地址

十、不同注册中心对比

特性EtcdZooKeeper
存储类型KV树结构
节点类型带租约临时/持久
心跳代码实现无需实现
监听WatchCache
一致性RaftZAB
易用性
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
longlong
下载 APP