跳到主要内容
📖 本章预览

本章为预览版本,展示部分核心内容。完整内容包含详细源码解析、实战代码和面试要点,加入知识星球即可解锁全部章节。

第六章:Naming 服务模型与数据结构

本章深入 Naming 模块的数据模型层。v2 架构的核心变化是:不再按 Service 管理实例,而是按 Client 管理——每个客户端手里有哪些实例一目了然。

v2数据模型对比

6.1 v1 vs v2 架构——为什么要重构

v1 的问题:以 Service 为中心,Service 对象持有 Instance 列表。

v1 数据模型:
Service("order-service")
└── instances: [192.168.1.1:8080, 192.168.1.2:8080, 192.168.1.3:8080]

当客户端断连时,服务端需要遍历所有 Service 找到该客户端注册的实例并摘除。假设有 1000 个 Service,每个 Service 有 100 个实例,最坏情况要遍历 10 万条数据。

v2 的解法:以 Client 为中心,Client 对象持有自己注册的所有 Service-Instance 映射。

v2 数据模型:
Client("10.0.1.5:38762#0")
└── publishers: {
Service("order-service") → InstancePublishInfo(192.168.1.1:8080),
Service("user-service") → InstancePublishInfo(192.168.1.1:8081)
}

客户端断连时,直接销毁 Client 对象,O(1) 完成所有实例摘除。

6.2 Service 值对象——不可变设计的精妙

public class Service implements Serializable {
private final String namespace;
private final String group;
private final String name;
private final boolean ephemeral;

private Service(String namespace, String group, String name, boolean ephemeral) {
this.namespace = namespace;
this.group = group;
this.name = name;
this.ephemeral = ephemeral;
}

// equals 只比较 namespace + group + name
// 注意:ephemeral 不参与比较!
@Override
public boolean equals(Object o) {
Service service = (Service) o;
return namespace.equals(service.namespace)
&& group.equals(service.group)
&& name.equals(service.name);
}

@Override
public int hashCode() {
return Objects.hash(namespace, group, name);
}
}

为什么所有字段都是 final?

  1. 线程安全:不可变对象天然线程安全,不需要加锁
  2. 可以安全做 HashMap 的 key:hashCode 不会变

6.3 ServiceManager——全局单例注册表

public class ServiceManager {
private static final ServiceManager INSTANCE = new ServiceManager();
private final ConcurrentHashMap<Service, Service> singletonRepository;
private final ConcurrentHashMap<String, Set<Service>> namespaceSingletonMaps;
}

为什么用 ConcurrentHashMap<Service, Service> 而不是 Set

public Service getSingleton(Service service) {
Service result = singletonRepository.computeIfAbsent(service, key -> {
NotifyCenter.publishEvent(new MetadataEvent.ServiceMetadataEvent(service, false));
return service;
});
// ...
}

computeIfAbsent 保证了原子性:如果 key 不存在,执行 lambda 创建 value 并放入;如果 key 已存在,直接返回已有的 value。全局只有一个 Service 实例,避免重复创建。


🔒 解锁完整内容

本章剩余内容需要解锁后查看

以上仅为本章部分预览内容,完整内容包含更多深度源码解析、实战代码和面试要点。

加入知识星球你将获得:

  • ✅ 全部 17 章完整内容 + 持续更新
  • ✅ 配套源码 + 实战项目
  • ✅ 一对一答疑 + 面试辅导
  • ✅ 简历优化 + 内推机会

📚 本章完整目录

以下为本章完整目录结构,加入知识星球即可解锁全部内容。

6.4 Client 模型——以连接为中心

6.5 InstancePublishInfo——实例数据

6.6 ClientServiceIndexesManager——双向索引

6.7 ServiceStorage——推送数据的组装

6.8 面试热点