← 返回博客
2026-09-30 08:00:01

Nacos源码解析:Distro协议如何撑起AP注册中心

Nacos源码解析:Distro协议如何撑起AP注册中心

一个常见的生产场景

假设你有一套 Nacos 集群,三个节点部署在北京、上海、广州。某个时刻,网络抖动导致上海节点和另外两个节点失联了。这时候会发生什么?

如果 Nacos 用的是 Raft(CP 模型),上海节点会发现选不出 Leader,整个集群可能拒绝服务,注册在里面的服务全部不可用。但实际用 Nacos 的人都知道——Nacos 默认不会这么干。上海节点依然能接受服务注册,本地服务依然能发现实例。这就是 Distro 协议在起作用。

Distro 是 Nacos 自研的 AP 型一致性协议,专门为"服务注册发现"这个场景设计。它和 Raft、Paxos 走的是完全不同的路子:不追求强一致,追求的是每个节点都能独立处理写请求,最终数据收敛。

Distro 的整体架构

先看一张简化后的数据流图:

          ┌─────────────────────────────────────────┐
          │           Nacos Cluster                  │
          │                                          │
          │   ┌──────────┐   ┌──────────┐   ┌──────────┐
          │   │ Node A   │   │ Node B   │   │ Node C   │
          │   │          │   │          │   │          │
          │   │ 责任分区  │   │ 责任分区  │   │ 责任分区  │
          │   │ [S1,S2]  │   │ [S3,S4]  │   │ [S5,S6]  │
          │   └────┬─────┘   └────┬─────┘   └────┬─────┘
          │        │              │              │
          │        └──── 异步同步 ─┴──────────────┘
          │                                          │
          └─────────────────────────────────────────┘

核心思想只有两条:

第一,每个节点负责一部分服务的写入。 服务按名字哈希,分配到某个节点作为"责任节点"。写请求发到哪个节点,如果这个节点不是责任节点,它会转发给责任节点。

第二,责任节点异步把数据同步给其他节点。 不等别人确认,自己先返回成功。

这就解释了前面那个场景:上海节点失联了,它负责的服务照常读写,只是数据暂时不会同步到其他节点。等网络恢复,数据再追上来。

核心类与调用链路

从服务注册的入口开始追。HTTP 请求打到 InstanceController,最终落到 DistroConsistencyServiceImpl:

// DistroConsistencyServiceImpl 核心结构
public class DistroConsistencyServiceImpl implements ConsistencyService {
    
    // 每个节点自己维护的数据副本
    private final Map<String, Datum> dataMap = new ConcurrentHashMap<>();
    
    // 待同步的任务队列(按责任节点分组)
    private final Map<String, BlockingQueue<String>> taskMap = 
        new ConcurrentHashMap<>();
    
    // 负责数据同步的执行器
    private TaskScheduler notifier;
    
    public void put(String key, Record value) throws NacosException {
        // 1. 先写本地
        onPut(key, value);
        // 2. 加入同步队列,异步发给责任节点
        notifier.addTask(key, DataOperation.CHANGE);
    }
}

调用链路可以概括成:

InstanceController.register()
  └─> DistroConsistencyServiceImpl.put(key, instance)
        ├─> onPut()  // 写本地 dataMap + 通知本地监听器
        └─> notifier.addTask(key, CHANGE)
              └─> DistroConsistencyServiceImpl.sync()
                    └─> DistroMapper.mapSrv(key)  // 找到责任节点
                          └─> NamingProxy.syncData()  // HTTP POST 到责任节点

这里有三个关键点。

onPut 先写本地再通知监听器。 本地服务发现能立刻感知到新实例,不用等同步完成。这是 AP 的直接体现。

DistroMapper.mapSrv() 决定数据归属。 它把服务名做哈希,映射到集群节点列表的某个位置。所有节点用同一份节点列表和同一个哈希算法,算出来的责任节点必然一致。这是无中心化分片的关键。

NamingProxy.syncData() 是 HTTP 调用,不是 RPC。 用 HTTP 的好处是简单、跨版本兼容,坏处是慢。所以同步是异步批量做的,不阻塞主流程。

同步与校验:数据怎么收敛

异步同步最大的问题是可能丢数据。比如责任节点收到同步请求前宕机了,或者网络分区导致部分节点没收到。Distro 用了两个机制兜底。

第一个是 sync 任务重试。 DistroConsistencyServiceImpl 里的 TaskScheduler 会把失败的任务重新入队,按固定间隔重试。

第二个是定时全量校验。 每个节点会定期向其他节点发起 checksum 请求,比较数据的校验和。不一致就触发全量同步。

// 校验逻辑简化示意
public void run() {
    // 计算本地所有数据的 checksum
    Map<String, String> checksumMap = getChecksum();
    // 发给其他节点比对
    for (String server : otherServers) {
        Map<String, String> remoteChecksum = 
            NamingProxy.getChecksum(server, checksumMap);
        // 找出不一致的 key,触发同步
        for (Map.Entry<String, String> entry : remoteChecksum.entrySet()) {
            if (!checksumMap.get(entry.getKey()).equals(entry.getValue())) {
                syncData(entry.getKey());
            }
        }
    }
}

校验和比对的设计很讨巧:不传全量数据,只传每个服务的哈希值。数据量大时也能快速定位差异。踩坑点在于 checksum 的计算频率——太频繁会给集群带来额外压力,太稀疏则数据不一致的窗口会拉长。Nacos 默认是 5 秒一次,生产环境需要根据集群规模调整。

为什么不用 Raft

这是理解 Distro 的关键。Raft 要求多数派确认,写延迟至少是一个 RTT。服务注册发现场景下,实例数量动辄上千,心跳上报每秒都在发生。如果每次注册都要多数派确认,集群吞吐会成为瓶颈。

Distro 的选择是:接受短暂的数据不一致,换取写入的高可用和低延迟。 服务发现有天然的容错性——某个消费者晚几秒看到新实例,通常不影响业务;但如果注册中心整体不可写,影响的是所有服务。

这是一种典型的"场景驱动设计":不是 Distro 比 Raft 更好,而是它更适合注册中心这个场景。

代价也很明确:Distro 不保证读到最新数据。极端情况下,两个节点可能对同一个服务返回不同的实例列表。Nacos 1.x 里这个问题更明显,2.x 通过 gRPC 长连接和更好的同步机制有所改善,但 AP 的本质没变。

下一步可以做什么

如果想深入理解 Distro,建议从 DistroConsistencyServiceImpl 的 sync() 方法入手,配合 DistroMapper 的哈希逻辑一起看。重点观察两件事:责任节点是怎么算出来的,以及同步失败后的重试策略。把这两块搞明白,Distro 的整体设计就清楚了。

本文关键词:Nacos、Distro协议、AP模型、服务注册、数据同步