数据库/协同工具类型
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 这样的中心统一管理:
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 到删除 = 实例下线。
用租约做服务注册的完整时序——「实例挂了自动摘除」的关键就在续租与过期:
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 --prefix,watch监听,lease租约,txn事务(可做 CAS)。 - 性能:纯内存 + 持久化,单集群几万 QPS,够协调场景用;不是为高吞吐数据存储设计的。
Raft 写入要多数派确认,这是 Etcd 强一致、不脑裂的根源:
五、常见问题与排查
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 选主的过程——抢到的为主,主挂了租约释放、其余再抢:
别用 Redis 做强一致选主(Redis 是 AP,网络分区可能选出两个主)。强一致选主用 Etcd/Zookeeper。
2. 一致性 vs 可用性(CAP)
| 系统 | 取向 | 含义 |
|---|---|---|
| Etcd / Zookeeper | CP | 分区时宁可拒服务也要一致——适合配置、选主(不能错) |
| Nacos(注册)/ Eureka | AP | 分区时各 partition 都能用——适合服务发现(宁可给旧实例,重试就行) |
服务发现选 AP(实例列表短暂不准、重试兜底,但不能因为注册中心挂了让整个调用链断);选主/配置选 CP。
3. Etcd vs Nacos vs Zookeeper 怎么选
| 维度 | Etcd | Nacos | Zookeeper |
|---|---|---|---|
| 一致性 | CP(Raft) | 注册 AP / 配置 CP | CP(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 + 客户端缓存本地兜底 |