跳转到主要内容

数据库/协同工具类型

Etcd 指南

Etcd 是什么、何时才需要、核心机制(强一致 KV / Raft / lease / watch / CP)、基础实操(Docker 起服务、etcdctl 增删改查 + watch + lease)、常见问题排查(watch 丢失、脑裂)、难点(分布式选主、一致性 vs 可用性、Etcd vs Nacos vs Zookeeper)与生产要点。

  • Etcd
  • 微服务

这是 Etcd 的完整文档:先讲清它是什么、什么时候才需要,再带你用 Docker 跑起来、用 etcdctl 敲通增删改查和 watch,最后是深度参考(核心机制、常见问题、选主、生产要点)。在 Nest 应用里用 Etcd 做配置/服务发现见 Nest 课程第二十二章

一、基础入门(从零开始)

这一节先讲清 Etcd 解决什么问题,再带你跑起来用 etcdctl;Nest 课程那篇讲的是在应用里封装 Etcd 客户端——两篇不重复。

1. 先搞懂:Etcd 是什么、什么时候才需要它

先说清楚它解决的问题。单体应用(一个程序包打天下)用 .env 存配置、代码里写死地址调别人,够用了。但一旦拆成微服务(十几个服务、每个多实例),两个新麻烦冒出来:

  • 配置改了要重启所有实例:数据库地址、限流阈值这些配置散在每台机器的 .env 里,改一处得改几十处、重启几十次。
  • 服务实例地址是动态的:实例会扩容/缩容、会挂、IP 总在变,调用方不可能写死 IP 去调「订单服务」。

于是需要两类基础设施:配置中心(配置集中存,改了实时推)和注册中心 / 服务发现(实例登记存活状态,调用方实时查)。

下面这张图对比两种模式——单体把配置和地址写死就行;微服务实例动态伸缩、地址总在变,必须靠 Etcd 这样的中心统一管理:

flowchart TB subgraph mono["单体:配置、地址都写死,够用"] M1[".env 存配置"] M2["代码里写死调用地址"] end subgraph micro["微服务:实例动态伸缩、地址总在变"] P1["订单服务 实例1"] P2["订单服务 实例2"] P3["用户服务"] end E((Etcd)) P1 -->|"1. 启动注册 + 定时续租"| E E -->|"2. watch 推送配置变更"| P2 E -->|"3. 返回用户服务存活地址"| P1

Etcd 就是干这个的底层工具——一个强一致的 key-value(KV)存储(基于 Raft 共识算法,CP,一致性优先)。它本身只提供「存 KV + 监听变更(watch)」,配置中心、注册中心、选主这些功能都在它之上搭。k8s 内部所有集群状态都存在 Etcd 里

核心概念:

  • KV 存储:一个 key 对一个 value,put /app/config/theme dark 存、get 取。
  • watch(监听):key 一变就通知客户端——配置推送、服务上下线感知都靠它。
  • lease(租约):给 key 绑租约,持有者定时续租,挂了不续租 key 就自动删——服务注册(实例挂了自动摘除)的关键。
  • Raft + CP:写要多数节点同意才算成功,分区时宁可拒服务也不丢一致性——适合存「不能错的」数据(配置、选主)。

一句话:没拆微服务就用不上 Etcd.env 够了);拆了微服务、要做配置中心/服务发现/选主这种「需要强一致协调」的事,才上 Etcd。

2. 用 Docker 起一个 Etcd

docker run -d --name etcd -p 2379:2379 \
  -e ALLOW_NONE_AUTHENTICATION=yes \
  bitnami/etcd:3.5
  • -p 2379:2379:2379 是 Etcd 客户端端口,应用连这个。
  • 应用连接地址:http://localhost:2379
  • docker logs etcd 没报错、能 get 到数据就算起来了。

3. 跟着敲:etcdctl 增删改查 + watch

进容器或本机装了 etcdctl(bitnami 镜像默认就是 API v3):

$ etcdctl put /app/config/theme dark      # 存一个 key
OK

$ etcdctl get /app/config/theme           # 取
/app/config/theme      # ← key
dark                   # ← value

$ etcdctl get /app --prefix               # 前缀查:拿 /app 下所有
/app/config/theme
dark

$ etcdctl watch /app/config/theme         # 监听变更(会阻塞)
# (开另一个终端执行:etcdctl put /app/config/theme light)
PUT                    # ← 这边立刻收到变更通知
/app/config/theme
light

$ etcdctl del /app/config/theme
1                      # ← 删了 1 个 key

→ 配置/注册信息都当 KV 存,改了用 watch 推送——这就是 Etcd 搭配置中心/服务发现的基础。

4. lease(租约):服务注册的关键

普通 put 的 key 是永久的。要让「实例挂了 key 自动消失」,用租约

$ etcdctl lease grant 30                       # 建一个 30 秒租约,返回 lease id
lease 694d... granted with TTL(30s)

$ etcdctl put /svc/node1 10.0.0.1 --lease=694d...   # 把 key 绑到这个租约
OK

# 真实服务会定时 keep-alive 续租;这里不续租,30 秒后:
$ etcdctl get /svc/node1                       # 30 秒后再查
# (空)— key 已随租约过期自动删 = 服务下线自动摘除

→ 这就是服务注册的套路:实例启动 put 一个带租约的 key,定时续租;实例崩了不续租,key 过期,调用方 watch 到删除 = 实例下线。

用租约做服务注册的完整时序——「实例挂了自动摘除」的关键就在续租与过期:

sequenceDiagram autonumber participant N as 订单服务实例 participant E as Etcd participant C as 调用方 N->>E: lease grant 30s + put /svc/order-1(绑租约) loop 每 10s 续租 N->>E: lease keep-alive end C->>E: get /svc/order --prefix E-->>C: 返回 order-1 地址 Note over N: 实例崩溃,停止续租 Note over E: 30s 后租约过期,key 自动删除 C->>E: watch /svc/order(已订阅) E-->>C: DELETE /svc/order-1

5. 基本使用规则

  • 没拆微服务别上.env 够用,引入 Etcd 是过度设计。
  • key 用 / 分层命名/app/config/theme/svc/order/node1,像文件路径,方便前缀查。
  • watch 要配全量兜底:断连重连后先全量拉一次再 watch,别只靠 watch(会漏变更)。
  • 强一致选主用 Etcd:别用 Redis(AP,可能选出两个主)。
  • 生产集群 ≥3 节点(奇数),单节点别上生产。

6. 核心词汇速记

术语一句话
配置中心配置集中存、改了实时推,不用改代码重启
注册中心 / 服务发现服务实例动态上下线,调用方实时查存活实例
KV 存储key-value,配置/注册信息都当 KV 存
lease / 租约给 key 设存活时间,持有者定时续租,挂了自动删
watch / 监听key 一变就通知客户端
Raft / CP共识算法,强一致,分区时宁可拒服务也不丢一致

二、作用与定位

Etcd 是强一致的 key-value 存储,本身只提供「存 KV + watch 监听」,但之上能搭出配置中心、注册中心、选主、分布式锁。k8s 内部所有集群状态都存在 Etcd 里——它是最关键的组件之一。

用途怎么用 Etcd 实现
配置中心配置存成 KV,应用 watch,一改就推
服务发现实例注册成带租约的 key,挂了自动摘,调用方 watch
选主多实例抢同一个 key,抢到的为主
分布式锁抢 key(NX + lease),用完释放

定位:强一致(CP)的协调底座。和 Nacos(带控制台、开箱即用)比,Etcd 更底层、要自己封装,但强一致、轻量、k8s 原生。

一句话:需要「强一致 + watch」的协调场景(配置/发现/选主/锁),尤其上 k8s,用 Etcd

三、何时该用 / 何时用别的

场景选择
单体 / 少量服务.env + 配置文件,不上 Etcd
多服务、要集中配置/发现Etcd(或 Nacos/Apollo/Consul)
已上 k8s配置用 ConfigMap,发现用 Service/k8s DNS(底层还是 Etcd)
想要带控制台、开箱即用Nacos

别为了用而用:3 个服务以内,.env + 负载均衡器就够了。

四、核心机制速览

  • 强一致(CP):基于 Raft 共识算法,写要多数节点同意才算成功,少数派分区自动只读、不会脑裂。
  • lease(租约)+ watch:key 绑租约、实例续租,挂了租约过期自动删;客户端 watch 感知变更。
  • 操作put/get/delete,前缀查 get --prefixwatch 监听,lease 租约,txn 事务(可做 CAS)。
  • 性能:纯内存 + 持久化,单集群几万 QPS,够协调场景用;不是为高吞吐数据存储设计的。

Raft 写入要多数派确认,这是 Etcd 强一致、不脑裂的根源:

flowchart LR CL["客户端 写"] --> L["Leader"] L --> F1["Follower 1"] L --> F2["Follower 2"] F1 -->|"确认"| L F2 -->|"确认"| L L -->|"多数派 2/3 达成 → 提交"| CL N["网络分区时:少数派凑不够多数 → 自动只读,选不出第二个主(不脑裂)"]

五、常见问题与排查

1. watch 丢失变更

原因:网络抖动断连,断连期间的变更没收到。

解决:客户端重连后做一次全量拉取 + 重新 watch(watch 带上 last-seen revision,断点续传)。别只依赖 watch、不做全量兜底。

2. 客户端配置缓存不更新

原因:配置在本地缓存了一份,但没监听变更或监听断了。

解决:启动时拉一次 + 订阅变更;变更回调里更新本地缓存 + 重新生效(如重建连接池)。注意配置热更新不是所有配置都支持(连接池大小改了要重建池)。

3. 网络分区 / 脑裂

原因:集群节点间网络断了,可能分裂出两个「主」。

解决:Etcd 是 CP——Raft 要求多数派,少数派分区自动降级为只读、不会选出第二个主,不会脑裂。关键的「选主」场景用 CP 系统(Etcd/Zookeeper),容忍短暂不可用换一致性。

六、难点与解决方案

1. 分布式选主

多个实例只能有一个「主」干活(如定时任务、消费分片)。用 Etcd 实现:

  • 各实例抢同一个 key(SETNX + lease),抢到的为主、其他 standby。
  • 主挂了 lease 过期、key 释放,其他实例再抢。
  • Etcd 的强一致保证「同时只有一个主」。

多个实例抢同一个 key 选主的过程——抢到的为主,主挂了租约释放、其余再抢:

sequenceDiagram autonumber participant A as 实例A participant B as 实例B participant E as Etcd par 同时抢占 A->>E: 抢 /leader(NX + lease) B->>E: 抢 /leader(NX + lease) end E-->>A: 成功 → 成为主 E-->>B: 失败 → standby,watch /leader Note over A: A 持有主,定时续租 lease Note over A,E: A 崩溃停止续租 → lease 过期,/leader 释放 E-->>B: watch 通知 /leader 变更 B->>E: 抢 /leader(NX + lease) E-->>B: 成功 → 新主,B 接管

别用 Redis 做强一致选主(Redis 是 AP,网络分区可能选出两个主)。强一致选主用 Etcd/Zookeeper。

2. 一致性 vs 可用性(CAP)

系统取向含义
Etcd / ZookeeperCP分区时宁可拒服务也要一致——适合配置、选主(不能错)
Nacos(注册)/ EurekaAP分区时各 partition 都能用——适合服务发现(宁可给旧实例,重试就行)

服务发现选 AP(实例列表短暂不准、重试兜底,但不能因为注册中心挂了让整个调用链断);选主/配置选 CP。

3. Etcd vs Nacos vs Zookeeper 怎么选

维度EtcdNacosZookeeper
一致性CP(Raft)注册 AP / 配置 CPCP(ZAB)
控制台内置 Web
易用性KV 原始,要自己封装API 友好、生态(Spring Cloud)重、复杂
适合k8s、需要 CP 原语Java/微服务、要可视化老系统(Dubbo/Kafka)

新项目:上 k8s → Etcd;Java 微服务 → Nacos;要选主/分布式协调 → Etcd 或 Zookeeper。

4. 配置热更新

配置改了怎么让运行中的应用生效?

  • 订阅模式:监听变更,回调里更新内存配置。
  • 注意「不能热更」的:数据库连接池大小、端口等改了要重启或重建资源——这类变更要配套重建逻辑,不是改个值就行。

七、生产环境要点

  • 集群 ≥3 节点(奇数,Raft 多数派),单节点别上生产。
  • 客户端要重连 + 全量兜底,别只靠 watch。
  • TTL / 心跳别设太长(实例挂了要尽快摘除),也别太短(网络抖动误判)。
  • 调用方做重试 + 健康检查,别盲信注册中心的实例列表。
  • 选主等强一致场景用 CP(Etcd),别用 Redis。
  • 监控:集群节点健康、leader 是否稳定、watch 连接数、key 数。
  • 定期备份:Etcd 是 k8s 的命根子,快照丢了整个集群就没了。

速查:常见问题对照

问题原因解决
watch 漏变更断连重连后全量拉 + 断点续 watch
配置不生效没订阅 / 没热更订阅变更、回调里重建资源
实例残留没正常注销 / 租约没到期TTL 自动摘 + 主动 deregister
选主选出多个用了 AP 系统(Redis)强一致选主用 Etcd/Zookeeper
Etcd 挂了全瘫单点集群 ≥3 + 客户端缓存本地兜底