4. 分布式协调服务:ZooKeeper核心原理、Paxos与Raft算法对比
说到分布式系统,有个绕不开的话题——协调服务。
你想想看,一堆机器凑在一起干活,谁当领导?谁说了算?节点挂了怎么办?这些事儿总得有个规矩。ZooKeeper就是干这个的。我最早接触它是在2014年,当时公司要做一套配置中心,选型时在etcd和ZooKeeper之间纠结了很久。最后选了ZK,原因很简单——社区成熟,踩坑的人多,文档也全。
4.1 ZooKeeper到底解决了什么问题?
说白了,分布式系统最怕的就是「脑裂」。一群节点互相不知道对方死活,各自为政,数据就乱套了。
ZooKeeper提供了一套原语:
- 临时节点:客户端挂了,节点自动消失。用来做心跳检测再合适不过。
- 顺序节点:自动编号,谁先来谁后到,一清二楚。
- Watch机制:节点变了,立马通知你。不用轮询,省带宽。
我习惯把ZooKeeper比作一个「分布式文件系统+通知中心」的合体。你往里面写数据,它帮你保证一致性;数据变了,它通知你。就这么简单。
核心要点:ZooKeeper不存业务数据,它存的是「元信息」——谁活着、谁当主、配置长什么样。别拿它当数据库用,会出事的。
4.2 ZAB协议:ZooKeeper的命根子
ZooKeeper内部跑的是ZAB协议(ZooKeeper Atomic Broadcast)。很多人以为ZAB就是Paxos的变种,其实不完全对。
ZAB的核心是两个模式:
- 崩溃恢复:Leader挂了,重新选一个。选完还要把数据同步好。
- 消息广播:Leader活着的时候,所有写请求都走它,它广播给所有Follower。
我记得有一次线上ZK集群出了点问题,Leader频繁切换。查了半天,发现是网络抖动导致Follower收不到心跳。嗯,这里要注意——ZAB的选举超时时间别设太短,否则网络一抖就选主,集群就一直在「恢复→广播→再恢复」的死循环里打转。
避坑指南:我曾经把tickTime设成100ms,结果生产环境一压测,Leader每30秒换一次。后来改成500ms,稳如老狗。别迷信默认值,要根据网络延迟调。
4.3 Paxos vs Raft:两个共识算法的恩怨情仇
聊分布式协调,绕不开这两个算法。Paxos是祖师爷,Raft是后来者。但说实话,Paxos太难懂了。Lamport老爷子写的论文,我看了三遍才勉强搞明白。
| 对比维度 | Paxos | Raft |
|---|---|---|
| 理解难度 | 高(论文抽象) | 低(拆成子问题) |
| 角色划分 | Proposer、Acceptor、Learner | Leader、Follower、Candidate |
| 选主方式 | 多轮投票,可能活锁 | 超时触发,随机超时防活锁 |
| 日志复制 | 两阶段提交(Prepare/Accept) | Leader统一追加,Follower确认 |
| 工程实现 | 少(只有Chubby等少数实现) | 多(etcd、Consul、TiKV都用它) |
你看这个表就明白了。Paxos更像一个数学证明,告诉你「共识是可行的」。Raft则是一份工程蓝图,告诉你「具体怎么干」。
我个人习惯这样理解:Paxos是「大家轮流提方案,谁先被多数人接受谁就赢」。Raft是「先选个班长,班长说了算,班长不行就换人」。哪个好实现?显然是后者。
4.4 Raft的日志复制到底怎么玩的?
Raft把问题拆成了三个子问题:选主、日志复制、安全性。咱们重点看日志复制。
流程其实不复杂:
- 客户端发请求给Leader。
- Leader把请求写成一条日志,追加到自己的日志里。
- Leader把这条日志发给所有Follower。
- Follower收到后也追加到自己的日志里,然后回复「收到」。
- Leader收到多数Follower的确认后,把这条日志标记为「已提交」,然后执行它。
- Leader通知Follower:这条日志可以提交了。
你可能会问:如果Leader在步骤3挂了怎么办?嗯,这就是Raft的精髓——新Leader会检查所有Follower的日志,确保不会丢数据,也不会乱序。
注意:Raft保证的是「已提交的日志不会丢」,但没保证「未提交的日志不会丢」。如果你的业务要求「写进去就必须持久化」,那得等Leader返回「已提交」才能算数。别提前给客户端说「写成功了」。
4.5 选主时的「任期」和「随机超时」
Raft里有个概念叫Term(任期)。每个任期最多只有一个Leader。选主时,Candidate会给自己加一个任期号,然后拉票。
如果两个Candidate同时发起选举,票数分散了怎么办?Raft用了随机超时——每个Follower等待超时的时间是随机的(比如150ms到300ms之间)。这样大概率只有一个Candidate先超时,先拉票,先当上Leader。
我曾经在模拟环境里试过,把随机超时范围缩小到10ms,结果连续三次选举都打平了。后来改成150-300ms,一次搞定。你看,有时候「随机」比「精确」更靠谱。
4.6 ZooKeeper vs etcd:选型建议
现在市面上主流的协调服务就两个:ZooKeeper(Java生态)和etcd(Go生态)。
- ZooKeeper:老牌、稳定、功能丰富。适合Java技术栈、对一致性要求极高的场景。缺点是运维复杂,JVM调优是个坑。
- etcd:轻量、Raft原生实现、API友好(gRPC+JSON)。Kubernetes就是用它做存储后端的。如果你在云原生环境里混,etcd是首选。
我个人建议:新项目选etcd,老项目如果已经用了ZK就别折腾迁移了。毕竟「能跑就别动」是分布式系统运维的第一原则。
一句话总结:Paxos是理论,Raft是实践,ZooKeeper是Raft的「老大哥」ZAB协议的实现。搞懂Raft,你就搞懂了现代分布式协调服务的半壁江山。
好了,这一章就到这儿。下一章咱们聊聊分布式存储——怎么把数据分片、怎么保证高可用、怎么做到弹性扩缩容。到时候我会拿一个真实案例来讲,保证比教科书有意思。