ZooKeeper是用于分布式应用程序的高性能协调服务
Coordinating Distributed Systems
zookeeper基础知识
ZooKeeper 的数据模型是层次模型, 层次模型常见于文件系统。
ZooKeeper 的层次模型称作 data tree。Data tree的每个节点叫作 znode。
znode 可以是持久节点也可以是临时节点。还可以是序列节点。
zk集群
zk集群角色有leader、follower、Observer。在集群中leader负责写和查询操作,follower和Observer负责查询操作。zk有2中运行状态:1.可用状态、2。不可用状态。不可用状态是leader宕机之后zk处于不可用状态,不提供任何操作,直到新的leader选举出来,其中新的leader会从follower中重新选举出来,Observer不参与leader的选举。因此从不可用状态恢复到可用状态应该越快越好(官方给出7台机器zk集群只需不到200毫秒即可选出新的领导者)。
zk应用场景
1、统一配置管理: 节点可以存储1M数据
2、分组管理: data tree模型
3、统一命名: sequential节点
4、分布式锁: 临时节点
5、HA、选主
zk特征
1、顺序一致性 - 客户端的更新将按发送顺序应用,服务端处理由单个节点顺序执行 FIFO。
2、原子性 - 更新成功或失败。没有部分结果。
3、统一视图 - 无论服务器连接到哪个服务器,客户端都将看到相同的服务视图。
4、可靠性 (持久性)- 一旦应用了更新,它将从那时起持续到客户端覆盖更新。
Paxos算法
zk的cap
zk能够保证扩展性、数据最终一致性、不能完全保证可用性。
扩展性:leader、follower、Observer 从少数节点中选举出leader。
可用性:leader宕机之后,zk集群处于不可用状态。当重新选举出leader保证可用性。
数据一致性:ZAB算法。过半通过。
zk选主
zk中leader宕机之后由集群中follower参与选举出新的leader,选举算法是ZAB算法。依据是zxid与myid。zxid由leader自增的事务id,myid集群中每个节点都有的一个id。当某个节点中发现leader宕机之后,会启动选举机制,此时集群对外不可用,每个follower都会把自己zxid与myid广播出去,接收到其他follower的信息包之后会比较zxid的大小,如果小于自己的zxid便反对反之则支持。超过半数会成为leader。但是其中会出现一种强开有n个节点的zxid支持票数相同这个时候通过对比myid。
扩展
zk相对于Redis实现分布式锁的优势。
在高并发场景下一般都会使用集群部署。Redis与zk都是使用缓存来存储数据的。因为zk的ZAB算法的过半机制导致它的性能会弱于Redis,但是zk能够保证数据的一致性。Redis的读写分离不能够保证数据的一致性,因为Redis备库从主库同步数据是异步的,所以分布式锁使用zk会合适一点。
Chubby
Google Chubby是一个分布式锁服务,Chubby底层一致性实现就是以Paxos为基础的。
相同点:
• Chubby的文件相当于ZooKeeper的znode。Chubby的文件和znode都是用来存储少量数据。
• 都只提供文件的全部读取和全部写入。
• 都提供类似UNIX文件系统的API。
• 都提供接收数据更新的通知,Chubby提供event机制,ZooKeeper提供watch机制。
• 它们的客户端都通过一个session和服务器端进行交互。
• 写操作都是通过leader/master来进行。
• 都支持持久性和临时性数据。
• 都使用复制状态机来做容错。
不同点:
锁机制:
1、 Chubby 内置对分布式锁的支持。
2、 ZooKeeper 本事不提供锁,但是可以基于 ZooKeeper 的基本操作来实现锁。
读操作 :
1、 读操作也必须通过 master 节点来执行。相应的,Chubby 保证的数据一致性强一些,不会有读到旧数据的问题。
2、 读操作可以通过任意节点来执行。相应的,ZooKeeper 保证的数据一致性弱一些,有读到旧数据的问题。
cache :
1、 Chubby 提供一个保证数据一致性的 cache 。有文件句柄的概念。
2、 ZooKeeper 不支持文件句柄,也不支持 cache,但是可以通过watch 机制来实现 cache 。但是这样实现的 cache 还是有返回旧数据的问题。
协议 :
1、 Chubby 使用 Paxos 数据一致性协议。
2、 ZooKeeper 使用 Zab 数据一致性协议。
Etcd
etcd 是一个高可用的分布式 KV 系统,可以用来实现各种分布式协同服务。etcd 采用的一致性算法是 raft,基于 Go 语言实现。
MVCC
etcd 的数据模型是 KV模型,所有的 key 构成了一个扁平的命名空间,所有的 key 通过字典序排序。
整个 etcd 的 KV 存储维护一个递增的64位整数。etcd 使用这个整数位为每一次 KV 更新分配一个 revision。每一个key 可以有多个 revision。每一次更新操作都会生成一个新的 revision。删除操作会生成一个 tombstone 的新的revision。如果 etcd 进行了 compaction,etcd 会对 compaction revision 之前的 key-value 进行清理。整个KV 上最新的一次更新操作的 revision 叫作整个 KV 的 revision。
CreateRevision 是创建 key 的 revision;ModRevsion 是更新 key 值的 revision;Version 是 key 的版本号,从1 开始。
数据存储
1、etcd使用bbolt进行KV的存储。bbolt使用持久化的B+tree保存key-value。三元组(major、sub、type)是 B+tree 的 key,major 是的 revision,sub 用来区别一次更新中的各个 key,type 保存可选的特殊值(例如 type 取值为 t 代表这个三元组对应的是一个tombstone)。这样做的目的是为加速某一个 revision 上的 range 查找。
2、 另外etcd还维护一个in-memory的B-tree索引,这个索引中的key是key-value中的key 。
etcd 和 ZooKeeper 覆盖基本一样的协同服务场景。ZooKeeper 因为需要把所有的数据都要加载到内存,一般存储几百MB的数据。etcd使用bbolt存储引擎,可以处理几个GB的数据。
Watch
Watch API 提供一个监控 etcd KV 更新事件的机制。etcd Watch 可以从一个历史的 revision 或者当前的 revision 开始监控一个 key range 的更新。
ZooKeeper 的 Watch 机制只能监控一个节点的当前时间之后的更新事件,但是 ZooKeeper 的Watch 支持提供了对子节点更新的原生支持。etcd 没有对应的原生支持,但是可以用通过一个key range 来监控一个目录下所有子孙的更新。
对比
ZooKeeper 和 etcd 都是优秀的分布式协同服务平台,都有很大的生态圈。
1、 ZooKeeper更成熟,系统更稳定,文档更加完备。在大数据生态,ZooKeeper是首选。如果研发首选语言是基于 JVM 的,建议 ZooKeeper。
2、 etcd的架构更先进一些。在云计算领域,etcd是首选。如果研发首选语言是Go,建议etcd。
源码
zk采用流水线模式(责任链模式),其中定义了很多Processor,这些Processor是一个线程,分别去执行不同的流程。
1、 Follower处理读请求源码解读
a、 FollowerRequestProcessor.run() ---> Request request = queuedRequests.take();nextProcessor.processRequest(request)
b、 CommitProcessor.run()---> request = queuedRequests.poll();sendToNextProcessor(request)
c、 FinalRequestProcessor.processRequest()---> cnxn.sendResponse()2、 Follower处理写请求源码解读
a、 FollowerRequestProcessor.run() ---> Request request = queuedRequests.take();nextProcessor.processRequest(request);zks.getFollower().request(request)
b、 CommitProcessor.run()---> request = queuedRequests.poll(); processCommitted() ;需要等待leader同意sendToNextProcessor(request)
c、SyncRequestProcessor.run()
d、SendAckRequestProcessor.processRequest()
e、 FinalRequestProcessor.processRequest()---> cnxn.sendResponse()