Clipping 微信公众号

看透 Nacos 3.x 注册中心:一次注册背后的 6 个关键设计

by Fox爱分享 原文 ↗
Created: 2026-07-02

公众号名称:Fox爱分享

作者名称:Fox爱分享

发布时间:2026-07-02 17:30

阿里开源了 Nacos 3.2,AI 功能加了一堆,但很多人还没搞明白注册中心这个老本行。这篇文章,我们把源码翻出来,把一次服务注册走一遍,你就全懂了。


开篇:一个问题

你的 Spring Boot 服务启动,注解一加,服务就被 Nacos 发现了。

但你有没有想过——这个”被发现”,背后到底发生了什么?

  • 实例信息存在哪儿?内存还是数据库?

  • 多个 Nacos 节点,数据怎么同步?

  • 服务挂了,Nacos 怎么知道?

  • 同一个服务,为什么有时候 AP,有时候 CP?

把这 4 个问题答清楚,Nacos 注册中心你就吃透了。


核心架构:Client-Service 分离模型

Nacos 3.x 的 naming 模块做了一次重大重构,从 v1 的”以服务为中心”,升级为 v2 的”以客户端为中心”的架构。

核心抽象就两个:

Client(客户端)      ←→      Service(服务)
持有实例发布信息              服务元数据/路由
持有订阅关系                  实例列表索引

这个设计的好处在于:连接即状态。一个 gRPC 长连接断了,这个 Client 下面的所有临时实例自动摘除,不需要额外的心跳超时判断。


第一层:服务存在哪里?

先看 ServiceManager,它是整个注册中心的”服务目录”:

public class ServiceManager {

    // 单例模式,全局唯一
    privatestaticfinal ServiceManager INSTANCE = new ServiceManager();

    // 核心存储:Service → Service(保证对象唯一性)
    privatefinal ConcurrentHashMap singletonRepository;

    // 按 namespace 做二级索引
    privatefinal ConcurrentHashMap> namespaceSingletonMaps;

    public Service getSingleton(Service service) {
        // computeIfAbsent 保证线程安全地创建服务
        Service result = singletonRepository.computeIfAbsent(service, key -> {
            // 服务首次创建,发布元数据事件
            NotifyCenter.publishEvent(new MetadataEvent.ServiceMetadataEvent(service, false));
            return service;
        });
        // 同步更新 namespace 索引
        namespaceSingletonMaps
            .computeIfAbsent(result.getNamespace(), ns -> new ConcurrentHashSet<>())
            .add(result);
        return result;
    }
}

几个设计要点值得注意:

① 初始容量 1024(1 << 10):经验值,减少扩容。大多数项目的服务数量在百到千级别,一次 resize 的代价比较高。

② computeIfAbsent 而不是 putIfAbsent:前者只有 key 不存在时才执行 lambda,避免重复创建对象;后者需要先创建对象再放入,存在无效创建的浪费。

③ 一发即忘的事件驱动NotifyCenter.publishEvent 是异步的,服务创建和元数据处理解耦,不会因为下游慢而阻塞注册流程。


第二层:实例注册的完整链路

一次实例注册,从 HTTP/gRPC 入口到最终存储,经历这几层:

客户端 SDK
   ↓ gRPC InstanceRequest
InstanceRequestHandler(gRPC 处理器)

InstanceOperatorClientImpl.registerInstance()

EphemeralClientOperationServiceImpl.registerInstance()

Client.addServiceInstance()  ←  实例落地

NotifyCenter.publishEvent()  ←  触发 Distro 同步

核心实现在 EphemeralClientOperationServiceImpl

@Override
public void registerInstance(Service service, Instance instance, String clientId)
    throws NacosException {
    // 1. 合法性校验(IP格式、端口范围、权重等)
    NamingUtils.checkInstanceIsLegal(instance);

    // 2. 获取/创建服务单例(保证全局唯一)
    Service singleton = ServiceManager.getInstance().getSingleton(service);

    // 3. 持久服务不能注册临时实例(类型保护)
    if (!singleton.isEphemeral()) {
        thrownew NacosRuntimeException(NacosException.INVALID_PARAM,
            "Current service is persistent, can't register ephemeral instance.");
    }

    // 4. 找到对应的 Client(gRPC连接对应的客户端对象)
    Client client = clientManager.getClient(clientId);
    checkClientIsLegal(client, clientId);

    // 5. 实例写入 Client 内存(这里才是真正的存储)
    InstancePublishInfo instanceInfo = getPublishInfo(instance);
    client.addServiceInstance(singleton, instanceInfo);
    client.setLastUpdatedTime();
    client.recalculateRevision();  // 更新数据版本号

    // 6. 发布事件,触发:①Distro集群同步 ②订阅者推送
    NotifyCenter.publishEvent(
        new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
    NotifyCenter.publishEvent(
        new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}

有一个细节很重要:实例是存在 Client 上的,不是存在 Service 上的

查询某个服务的实例列表时,是通过 ServiceStorage 反向索引找到所有注册了该服务的 Client,再聚合实例信息。这个设计让连接断开时的清理极其简单——删掉 Client 对象,所有实例自动消失。


第三层:AP vs CP,谁来决定?

这是最多人困惑的地方。

Nacos 同时支持两种一致性协议:


AP(Distro)CP(JRaft)
实例类型临时实例(ephemeral=true)持久实例(ephemeral=false)
一致性最终一致强一致
故障处理节点宕机,其负责实例自动转移Leader 选举,少数服从多数
适用场景微服务注册(默认)需要强一致的配置型服务

临时实例走 Distro,这是注册中心的默认模式。

DistroClientDataProcessor 同时实现了两个接口:

public class DistroClientDataProcessor extends SmartSubscriber
    implements DistroDataStorage,   // 提供数据快照,用于节点间全量同步
               DistroDataProcessor { // 处理从其他节点收到的增量数据

    public static final String TYPE = "Nacos:Naming:v2:ClientData";

    // 监听 Client 操作事件,触发集群同步
    // ClientRegisterServiceEvent → 向其他节点同步新增实例
    // ClientDeregisterServiceEvent → 向其他节点同步删除实例
}

Distro 的同步逻辑:

  1. 写请求路由:每个节点负责一部分 Client(按 hash 分片),自己的 Client 自己处理

  2. 增量同步:有变更时,异步推送 DistroData 到其他节点

  3. 全量同步:新节点加入,拉取全量快照,这就是 isFinishInitial 标志位的作用


第四层:健康检查,两种姿势

姿势一:连接健康即实例健康(临时实例)

这是 Nacos 3.x 最优雅的设计。gRPC 长连接本身就是心跳载体:

  • 连接活着 → Client 活着 → 实例活着

  • 连接断开 → 触发 ClientDisconnectEvent → Client 下所有实例自动注销

不需要额外的心跳线程,连接即心跳。

姿势二:主动心跳检测(兼容旧版 HTTP 注册)

ClientBeatCheckTaskV2 处理 HTTP 方式注册的旧版实例,通过定时任务检查心跳超时:

@Override
public void doHealthCheck() {
    Collection services = client.getAllPublishedService();
    for (Service each : services) {
        HealthCheckInstancePublishInfo instance =
            (HealthCheckInstancePublishInfo) client.getInstancePublishInfo(each);
        // 经过拦截器链,最终判断心跳是否超时
        interceptorChain.doInterceptor(
            new InstanceBeatCheckTask(client, each, instance));
    }
}

拦截器链(InstanceBeatCheckTaskInterceptorChain)是个责任链模式,可以插入自定义逻辑,比如自定义健康检查策略。


第五层:订阅推送,事件驱动

消费者订阅服务后,注册中心怎么把变更推过去?

答案是事件驱动 + gRPC 服务端推送:

实例变更
  ↓ NotifyCenter.publishEvent(ClientOperationEvent)
NamingSubscriberServiceV2Impl(订阅者通知服务)
  ↓ 找出订阅了该服务的所有 Client
  ↓ 遍历,组装 ServiceChangedEvent
gRPC 连接推送

消费者客户端更新本地缓存

整条链路全异步,发布者不感知消费者的存在,系统解耦做得非常干净。


第六层:Namespace + Group,两级隔离

Namespace(命名空间)
  └── Group(服务分组)
        └── ServiceName(服务名)
              └── Instance(实例)
  • Namespace:最高隔离级别,不同 namespace 的服务互相不可见,适合环境隔离(dev/test/prod)

  • Group:同 namespace 内的逻辑分组,适合同环境下的业务隔离

ServiceManager 按 namespace 维护二级索引,查询时先过滤 namespace,再匹配服务名,这也是为什么用 namespaceSingletonMaps 做二级缓存的原因。


总结:注册中心的 6 个核心设计

设计点方案优势
服务存储ConcurrentHashMap 单例仓库线程安全,对象唯一
实例挂载实例挂在 Client 上连接断开即自动清理
一致性选择AP(Distro) / CP(JRaft) 按实例类型分流灵活适配不同场景
健康检查gRPC连接 + 心跳检测双轨向下兼容,覆盖全场景
变更推送事件驱动 + 服务端推送实时性高,解耦彻底
多租户Namespace + Group 两级隔离灵活的环境/业务隔离

最后说一句

很多人觉得注册中心”就那样”,其实里面的细节非常多。

Nacos 3.x 的 V2 架构比 V1 精妙在哪里?就在于那句”连接即状态”。把 gRPC 长连接的生命周期和实例的生命周期绑定在一起,系统复杂度直线下降,可靠性反而上去了。

这种”用约束换简单”的设计思想,才是最值得学的东西。


如果你觉得这篇文章有收获,欢迎转发给更多做分布式的朋友。下篇我们聊 Nacos 3.x 的 Distro 协议数据同步细节——那里才是真正硬核的部分。

关注「Fox爱分享」,持续输出分布式/云原生/AI工程硬核内容。


内容效果不满意?点此反馈

输入关键词开始搜索