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的核心是两个模式:

  1. 崩溃恢复:Leader挂了,重新选一个。选完还要把数据同步好。
  2. 消息广播: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把问题拆成了三个子问题:选主、日志复制、安全性。咱们重点看日志复制。

流程其实不复杂:

  1. 客户端发请求给Leader。
  2. Leader把请求写成一条日志,追加到自己的日志里。
  3. Leader把这条日志发给所有Follower。
  4. Follower收到后也追加到自己的日志里,然后回复「收到」。
  5. Leader收到多数Follower的确认后,把这条日志标记为「已提交」,然后执行它。
  6. 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,你就搞懂了现代分布式协调服务的半壁江山。

好了,这一章就到这儿。下一章咱们聊聊分布式存储——怎么把数据分片、怎么保证高可用、怎么做到弹性扩缩容。到时候我会拿一个真实案例来讲,保证比教科书有意思。