乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • ZooKeeper 学习路径
  • 第1章 分布式协调与ZooKeeper概述

    • ZooKeeper 概述
  • 第2章 单机与集群搭建

    • 配置参数详解
    • 集群搭建
  • 第3章 数据模型与ZNode

    • ZNode 详解
    • 节点类型对比
    • 顺序节点
    • ACL 权限控制
  • 第4章 会话与Watcher机制

    • 会话机制
    • Watcher 机制
  • 第5章 ZAB协议与一致性保证

    • ZAB 协议
    • 一致性保证
    • 数据同步
  • 第6章 Leader选举

    • Leader 选举
  • 第7章 典型应用:分布式锁

    • 分布式锁
  • 第8章 典型应用:配置中心与命名服务

    • 配置中心
    • 命名服务
  • 第9章 客户端编程基础(Java原生API)

    • Java 原生 API 编程
  • 第10章 运维与监控

    • 四字命令
    • 监控体系
  • 第11章 面试考点与设计思想

    • 设计思想
    • 面试考点汇编

本章定位:配置中心是 ZooKeeper 最广泛的应用。理解如何利用 ZNode + Watcher 实现配置的集中管理和动态热更新。

定义与作用

配置中心是分布式系统中统一管理配置信息的组件。所有服务实例从配置中心拉取配置,配置变更时自动推送更新。

ZooKeeper 作为配置中心的天然优势:

特性ZNode + Watcher 如何支撑
集中存储树形结构天然组织配置项(/config/app/db.url)
动态更新Watcher 机制实时推送变更通知
版本管理ZNode 的 version 字段追溯变更历史
高可用集群部署,单点故障不影响配置读取
权限控制ACL 限制敏感配置的访问

核心原理

配置中心架构

服务实例在启动时拉取配置并注册 Watcher。管理员修改配置后,所有实例收到通知并应用新配置,无需重启。

配置结构设计

推荐按 环境 / 应用 / 模块 / 参数 四级组织:

/config
  /prod                    # 生产环境
    /order-service         # 订单服务
      /db.url             # → jdbc:mysql://prod-db:3306/order
      /redis.host         # → 192.168.1.10
    /user-service
      /db.url
  /staging                # 预发环境
    /order-service
      /db.url

完整示例

示例一:命令行配置中心

场景说明:学生选课系统,动态切换数据库连接。

# 初始化配置树
zkCli.sh -server 127.0.0.1:2181

[zkshell: 0] create /config "config-root"
Created /config
[zkshell: 1] create /config/course "course-config"
Created /config/course
[zkshell: 2] create /config/course/db.url "jdbc:mysql://localhost:3306/course_dev"
Created /config/course/db.url
[zkshell: 3] create /config/course/db.pool "10"
Created /config/course/db.pool
[zkshell: 4] create /config/course/cache.ttl "300"
Created /config/course/cache.ttl
# 服务实例读取配置
[zkshell: 5] get /config/course/db.url
jdbc:mysql://localhost:3306/course_dev

[zkshell: 6] get /config/course/db.pool
10

# 注册 Watcher 监听所有配置
[zkshell: 7] get -w /config/course/db.url
...
# 运维切换到生产数据库
[zkshell: 8] set /config/course/db.url "jdbc:mysql://prod-db:3306/course"
# 服务实例收到 Watcher 通知:
# type:NodeDataChanged path:/config/course/db.url

操作前后对比:

配置项修改前修改后服务实例反应
db.urljdbc:mysql://localhost/course_devjdbc:mysql://prod-db/course重新获取并切换数据源
db.pool1010(未变)无变化
cache.ttl300300(未变)无变化

示例二:Java 配置监听器

场景说明:订单服务监听数据库连接配置变更。

public class ConfigCenter {
    private ZooKeeper zk;
    private String configPath = "/config/order/db.url";
    private volatile String dbUrl;

    public void init() throws Exception {
        zk = new ZooKeeper("127.0.0.1:2181", 5000, event -> {});

        // 初始化读取
        dbUrl = new String(zk.getData(configPath,
                new ConfigWatcher(), null));
        applyConfig();

        // 如有必要,批量加载所有配置项
        List<String> children =
                zk.getChildren("/config/order", false);
        for (String child : children) {
            String value = new String(
                zk.getData("/config/order/" + child,
                    false, null));
            System.out.println(child + " = " + value);
        }
    }

    class ConfigWatcher implements Watcher {
        @Override
        public void process(WatchedEvent event) {
            if (event.getType() ==
                    Event.EventType.NodeDataChanged) {
                try {
                    dbUrl = new String(
                        zk.getData(configPath, this, null));
                    applyConfig();
                    System.out.println("Config updated: "
                            + dbUrl);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void applyConfig() {
        // 重建数据库连接池等
        System.out.println("Applied new config: " + dbUrl);
    }
}

执行结果(配置变更时):

Applied new config: jdbc:mysql://localhost:3306/order_dev
Config updated: jdbc:mysql://localhost:3306/order_dev
Applied new config: jdbc:mysql://prod-db:3306/order

操作前后对比:

阶段db.url 值连接池状态
初始化jdbc:mysql://localhost/order_dev开发环境连接池
收到变更通知jdbc:mysql://prod-db/order重建 → 生产环境连接池

示例三:分组配置管理与灰度发布

场景说明:通过配置分组实现灰度发布,部分实例使用新配置。

# 默认配置
create /config/order/v1/db.url "jdbc:mysql://old-db:3306/order"

# 灰度配置
create /config/order/v2/db.url "jdbc:mysql://new-db:3306/order"
// 实例启动时检查自己的灰度标识
String configVersion = System.getProperty("config.version", "v1");
String configRoot = "/config/order/" + configVersion;
// v1 实例读旧配置,v2 实例读新配置

操作前后对比:

实例组配置版本db.url说明
稳定实例 (90%)v1old-db继续使用旧库
灰度实例 (10%)v2new-db验证新库

易错场景与面试考点

易错场景

1. Watcher 未重新注册导致配置"失联"

// 错误
byte[] data = zk.getData("/config/db.url", watcher, null);
// Watcher 触发后失效,后续变更不再通知
// 正确
byte[] data = zk.getData("/config/db.url", this, null);
// process() 中重新 getData(..., this, ...)

2. 配置项数量过多导致 Watcher 爆炸

每个配置项注册一个 Watcher,数千配置项会产生数千个 Watcher。解决方案:使用 Curator 的 TreeCache 统一监听父节点:

TreeCache cache = new TreeCache(client, "/config");
cache.start();
cache.getListenable().addListener((c, event) -> {
    // 统一处理所有子节点变化
});

3. 配置变更和应用生效之间的时间窗口

Watcher 通知后 → 重新 getData → 解析新值 → 应用新配置(重建连接池等)。这个过程中配置可能再次变更,导致"被跳过"的中间状态。应用层应做好幂等性处理。

4. 敏感配置明文存储

/config/app/db.password=root123 任何人都可读取(默认 ACL 为 world:anyone)。敏感配置应:

  • 设置 digest ACL
  • 或使用加密存储 + 应用层解密

面试高频题

Q:ZooKeeper 配置中心与 Apollo/Nacos 的区别?

A:

  • ZooKeeper:通用协调服务,配置中心是其应用之一。原生 API 较底层,需自行封装。
  • Apollo/Nacos:专用配置中心,提供 Web 管理界面、配置灰度、版本回滚、审计日志等开箱即用功能。
  • 选型:已有 ZooKeeper 的团队可直接使用;纯配置管理场景推荐 Apollo/Nacos。

Q:配置中心如何处理配置回滚?

A:

  1. 利用 ZNode 的 version 字段:记录每次变更的 version,回滚时 setData 到旧版本内容
  2. 备份策略:每次变更前将旧值写入 /config/backup/db.url.v{N},回滚时读取对应版本
  3. 使用 Curator 的 withVersion() 实现乐观锁回滚

Q:如何保证配置推送的最终一致性?

A:Watcher 通知 + 客户端主动拉取:收到 NodeDataChanged 后立即 getData 获取最新值。如果 Watcher 丢失(如网络抖动),可配合定时轮询(如每 60s 全量同步一次)作为兜底。

小结

要点说明
配置组织按环境/应用/模块/参数四级树形结构
动态更新Watcher 推送通知 + getData 主动拉取
灰度发布不同配置版本路径,实例按标识选择
版本管理version 字段追溯变更,支持乐观锁回滚
安全ACL 保护敏感配置,或应用层加密
轮询兜底定时全量同步弥补 Watcher 丢失

配置中心解决了分布式配置管理问题。下一节进入命名服务,理解如何用 ZNode 实现服务注册与发现。

下一页
命名服务