Nacos Configuration Service 设计原理 && 底层代码 详解

Nacos 的 Configuration Service(配置服务) 是其核心模块之一,用于实现动态配置管理,支持配置的集中管理、实时推送、版本控制、灰度发布等功能。它广泛应用于微服务架构中,替代传统的 application.propertiesapplication.yml 静态配置方式。

本文将深入解析 Nacos Configuration Service 的设计原理,并结合 Nacos 2.0+ 源码,从请求入口到数据存储、监听机制、长轮询推送等进行 底层代码详解


一、Nacos Configuration Service 核心功能

功能说明
配置管理支持增删改查配置(Data ID + Group + Namespace)
动态刷新配置变更后,自动推送到客户端,无需重启应用
环境隔离通过 Namespace 实现多环境(dev/test/prod)隔离
灰度发布支持按客户端 IP 或标签灰度推送配置
版本管理保留历史版本,支持回滚
权限控制基于 RBAC 的权限管理(需开启鉴权)
集群同步多节点间配置数据一致性同步

二、架构设计原理

1. 整体架构图

+----------------+     +------------------+     +------------------+
|   Client SDK     |<--->|   Nacos Server     |<--->|   Nacos Server     |
| (Spring Cloud    |     | (Config Module)    |     | (Config Module)    |
|  Alibaba, SDK)   |     +------------------+     +------------------+
+----------------+               |                          |
                                 v                          v
                         +------------------+     +------------------+
                         |   Data Storage     |     |   Data Storage     |
                         | (Derby/MySQL)      |     | (Derby/MySQL)      |
                         +------------------+     +------------------+

2. 核心组件

组件职责
ConfigController接收 HTTP 配置请求(发布、查询、监听)
ConfigService配置服务主逻辑,协调读写与通知
ConfigStorage配置持久化层(支持 Derby/MySQL)
LongPollingService实现长轮询,监听客户端配置变化
AsyncNotifyService异步推送配置变更给客户端
DumpService启动时从 DB 加载配置到内存
TenantCounterService管理命名空间下的配置数量
RaftCore / RaftConsistencyServiceImplRaft 协议实现,保证集群一致性(持久化数据)

⚠️ 注意:Nacos 配置服务 所有数据均使用 Raft 协议保证强一致性,不使用 Distro。


三、关键流程详解

1. 配置发布(Publish)

流程图
Client -> ConfigController -> ConfigService -> Raft -> DB + 内存 -> PushService -> 客户端
源码入口:ConfigController.publishConfig

文件:com.alibaba.nacos.config.server.controller.ConfigController

@PostMapping("/publishConfig")
public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,
                            @RequestParam("dataId") String dataId,
                            @RequestParam("group") String group,
                            @RequestParam("content") String content) throws NacosException {

    String namespaceId = WebUtils.optional(request, "tenant", "");
    String tag = WebUtils.optional(request, "tag", "");

    // 构建配置项
    ConfigItem configItem = new ConfigItem();
    configItem.setDataId(dataId);
    configItem.setGroup(group);
    configItem.setContent(content);
    configItem.setNamespaceId(namespaceId);
    configItem.setLastModifiedTime(System.currentTimeMillis());

    // 发布配置
    configService.publishConfig(configItem, tag);
    return true;
}

2. ConfigService.publishConfig

文件:com.alibaba.nacos.config.server.service.ConfigService

public void publishConfig(ConfigItem configItem, String tag) throws NacosException {
    // 1. 写入 Raft 日志(强一致性)
    raftConsistencyService.put(getConfigKey(configItem), configItem);

    // 2. 成功后持久化到 DB
    persistService.insertOrUpdate(configItem, tag);

    // 3. 触发通知(推送给所有监听该配置的客户端)
    notifyConfigListeners(configItem);
}

所有写操作必须通过 Raft 协议达成多数派确认,确保数据一致性。


3. Raft 一致性写入(raftConsistencyService.put)

文件:com.alibaba.nacos.consistency.raft.RaftConsistencyServiceImpl

@Override
public void put(String key, Record value) throws NacosException {
    // 将操作封装为 Raft Log
    LogEntry logEntry = new LogEntry();
    logEntry.setData(serialize(value));
    logEntry.setTerm(getTerm());

    // 提交日志(Raft 协议核心)
    boolean success = raftCore.submit(logEntry);
    if (!success) {
        throw new NacosException(NacosException.SERVER_ERROR, "Raft submit failed");
    }
}
  • raftCore.submit() 会广播日志到集群其他节点。
  • 多数节点确认后,提交日志,触发 onApply() 回调。

4. Raft 日志提交后:更新内存 + DB

文件:RaftCore.java

public void onApply(LogEntry entry) {
    Record record = deserialize(entry.getData());
    String key = record.getKey();

    // 更新内存缓存
    ConfigCacheService.onChange(key, record.getValue());

    // 持久化到数据库(异步)
    persistService.asyncInsertOrUpdate(record.getValue());
}
  • ConfigCacheService 是内存中的配置缓存(ConcurrentHashMap)。
  • 支持快速查询:ConfigCacheService.getContent(dataId, group, tenant)

5. 配置查询(Get)

文件:ConfigController.getConfig

@GetMapping("/getConfig")
public String getConfig(HttpServletRequest request) {
    String dataId = request.getParameter("dataId");
    String group = request.getParameter("group");
    String tenant = WebUtils.optional(request, "tenant", "");

    // 直接从内存缓存中读取
    String content = ConfigCacheService.getContent(dataId, group, tenant);
    if (content != null) {
        return content;
    }

    // 缓存未命中,从 DB 加载
    return persistService.findConfig(dataId, group, tenant);
}

读操作不走 Raft,直接读内存或 DB,性能极高。


四、动态推送机制:长轮询(Long Polling)

Nacos 客户端通过 长轮询(Long Polling) 实现配置变更的“实时”感知。

1. 客户端请求示例

GET /nacos/v1/cs/configs?listen=1&dataId=test&group=DEFAULT_GROUP&tenant=public
  • 客户端携带所有监听的配置的 md5 值。
  • 服务端对比,若有变更,立即返回;否则挂起最多 30 秒。

2. 服务端处理:LongPollingService

文件:com.alibaba.nacos.config.server.service.LongPollingService

public class LongPollingService extends ExecutorFactory implements Runnable {

    private final Map<String, AsyncLongPollingTask> clientList = new ConcurrentHashMap<>();

    @Override
    public void run() {
        // 定期检查是否有配置变更
        List<String> changedGroups = getChangedGroups();
        for (String groupKey : changedGroups) {
            // 通知所有监听该 group 的客户端
            for (AsyncLongPollingTask task : clientList.values()) {
                if (task.isListening(groupKey)) {
                    task.sendResponse(); // 触发 HTTP 响应
                    clientList.remove(task.clientId);
                }
            }
        }
    }
}
  • 启动一个后台线程,每 5 秒检查一次配置变更。
  • 使用 GlobalExecutor.scheduleLongPolling() 定期执行。

3. AsyncLongPollingTask:异步长连接任务

class AsyncLongPollingTask {
    private ServletRequest request;
    private ServletResponse response;
    private Map<String, String> listenMd5Map; // 客户端监听的配置及其 md5

    public void check() {
        for (Map.Entry<String, String> entry : listenMd5Map.entrySet()) {
            String groupKey = entry.getKey();
            String clientMd5 = entry.getValue();
            String serverMd5 = ConfigCacheService.getMd5(groupKey);

            if (!clientMd5.equals(serverMd5)) {
                sendResponse(); // 立即返回变更
                return;
            }
        }

        // 未变更,挂起最多 30 秒
        request.getAsyncContext().setTimeout(30000);
    }
}

当配置变更时,ConfigService.notifyConfigListeners() 会触发 LongPollingService 检查。


4. 配置变更通知链路

publishConfig()
  → Raft 提交
  → ConfigCacheService.onChange()
  → NotifyService.notifySubscriber()
  → LongPollingService.wakeup()
  → AsyncLongPollingTask.sendResponse()
  → 客户端收到 304 或新配置

五、数据模型与存储结构

1. 配置主表:config_info

字段说明
data_id配置 ID,如 application.yml
group分组,默认 DEFAULT_GROUP
content配置内容
md5内容 MD5,用于比对变更
tenant_id命名空间(Namespace)
gmt_modified修改时间

2. 历史表:config_info_beta / config_info_tag / his_config_info

  • his_config_info:保留历史版本,支持回滚。
  • config_info_beta:灰度发布专用。
  • config_info_tag:标签路由配置。

六、灰度发布(Beta 发布)

支持对指定 IP 的客户端发布配置。

// 发布 Beta 配置
configService.publishConfigBeta(dataId, group, content, "192.168.1.100,192.168.1.101");
  • 写入 config_info_beta 表。
  • 客户端请求时,若 IP 在 Beta 列表中,优先返回 Beta 配置。

七、启动时加载配置(Dump)

Nacos 启动时,从 DB 加载所有配置到内存缓存。

文件:DumpService.java

@PostConstruct
public void dumpAll() {
    List<ConfigInfo> configs = persistService.findAllConfigInfo();
    for (ConfigInfo config : configs) {
        ConfigCacheService.loadFromDb(config); // 加载到内存
    }
}
  • 使用分页加载,避免 OOM。
  • 支持集群模式下从 Leader 节点同步。

八、设计亮点总结

特性说明
强一致性所有写操作通过 Raft 协议保证 CP
高性能读配置常驻内存,读操作 O(1)
实时推送基于 Long Polling + 异步通知,延迟低
多环境隔离Namespace + Group 实现多维度隔离
可追溯历史版本、操作日志完整保留
高可用集群部署,自动选主,故障恢复

九、与 Eureka / Apollo 对比

特性NacosEurekaApollo
服务发现
配置管理
一致性模型Raft(CP)APMySQL + 长轮询
推送机制Long Polling客户端拉取HTTP Long Polling
多环境支持
灰度发布

Nacos = Eureka + Config Server + Apollo 轻量版


十、参考资料

  1. Nacos 官网:https://nacos.io
  2. GitHub 源码:https://github.com/alibaba/nacos
  3. Nacos 架构设计文档:Architecture
  4. Raft 协议论文:In Search of an Understandable Consensus Algorithm
### Nacos 底层架构与工作原理详解 Nacos底层架构基于分布式系统设计原则,结合了服务注册、配置管理、健康检查和一致性协议等核心技术,能够支持大规模微服务架构下的动态服务治理需求。其核心模块包括 **Naming Service(命名服务)** 和 **Config Service(配置服务)**,分别负责服务发现和配置管理功能[^4]。 #### 1. 架构组成 Nacos 的整体架构由多个关键组件构成: - **Nacos Server**:作为服务提供者,包含 Open API 接口用于外部访问,以及 Naming ServiceConfig Service 模块。 - **Consistency Protocol**:采用 Raft 算法保证集群节点间的数据一致性,确保高可用性与数据同步。 - **Name Server**:通过 VIP 或 DNS 实现服务路由,支持服务消费者快速定位服务实例[^5]。 - **Nacos Console**:提供可视化操作界面,支持服务管理、配置发布等功能。 该架构支持单机模式和集群部署,适用于从开发测试到生产环境的多种场景。 #### 2. 服务注册与发现机制 在服务注册过程中,服务提供者(Provider APP)启动时会向 Nacos Server 发送注册请求,包含元数据信息如 IP 地址、端口、健康状态等。Nacos Server 将这些信息存储在内存中,并通过一致性协议(Raft)同步至其他节点[^4]。 服务消费者(Consumer APP)通过调用 Naming Service 查询服务实例列表,依据负载均衡策略选择目标实例发起调用。Nacos 支持多种服务发现方式,包括 REST、DNS 和 RPC 协议,适配主流微服务框架[^1]。 #### 3. 健康检查机制 Nacos 提供主动和被动两种健康检查方式: - **主动检查**:Nacos 定期向服务实例发送心跳请求,若未收到响应则标记为不健康。 - **被动检查**:服务实例定期上报心跳信息,若连续多次未上报,则被判定为宕机并从服务列表中移除。 健康检查机制保障了服务发现结果的准确性,避免将请求转发至不可用的服务节点[^1]。 #### 4. 动态配置管理实现 Nacos 的配置中心通过 Config Service 实现,支持配置的集中管理和实时更新。应用通过 SDK 集成后,可以在运行时监听配置变更事件,一旦配置发生修改,SDK 会自动拉取最新配置并刷新本地缓存,实现“热更新”[^4]。 配置文件以 DataId 和 Group 为标识进行组织,用户可通过控制台或 API 对配置进行增删改查操作。为了提升性能和扩展能力,Nacos 支持配置的版本管理、加密存储及灰度发布功能[^1]。 #### 5. 数据一致性与集群部署 Nacos 集群使用 Raft 算法实现节点间的数据同步,确保写操作在多数节点确认后才生效,从而保证强一致性。每个集群节点可担任 Leader 或 Follower 角色,Leader 负责处理写请求并将日志复制给 Follower[^4]。 在部署方面,Nacos 支持多节点集群模式,通常建议至少三台服务器组成集群以提升容灾能力。集群节点通过 `cluster.conf` 文件配置彼此的通信地址和端口,启动时指定 `-m cluster` 参数启用集群模式[^1]。 #### 6. 多租户与权限控制 Nacos 支持多租户管理,通过 Namespace 实现资源隔离。每个租户拥有独立的配置和服务注册空间,互不影响。权限控制基于 RBAC 模型,管理员可以创建用户、角色并分配权限,确保不同用户对资源的访问受到严格限制[^1]。 例如,普通用户可能只能查看服务信息,而管理员则具备修改配置、删除服务等高级权限,提升了系统的安全性与可控性。 #### 7. 存储机制与数据库支持 默认情况下,Nacos 使用内嵌的 Derby 数据库存储元数据信息。但在生产环境中,推荐使用 MySQL 等关系型数据库进行持久化存储。通过修改 `application.properties` 中的数据库连接参数,即可切换至外部数据库,提升数据可靠性与可维护性。 ```properties spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true db.user=root db.password=your_password ``` 该机制支持多种数据库类型,增强了系统的灵活性和适应性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值