概述
Zookeeper(简称zk)是一个用于分布式应用程序协调的开源服务。Zookeeper本质上是一个key-value的数据库,其中key是一种递归的树形目录形式key,且在分布式部署场景能够保障各节点数据的一致性。相对于key-value数据库redis,zookeeper不适合与更新频繁、数据量大的场景。
用途上主要有:
l配置管理场景:将配置存储在zookeeper中,所有的修改都能保障最终的一致性,同时会及时将修改通知配置的使用者。
l命名服务场景:与配置管理的原理相同,但是场景不同而已。同时命名服务的可能更多是使用者来拉取而不是进行推送通知。
l分布式的订阅/通知场景。
lMaster选举:比如kafka中或者hadoop中。
l分布式锁。一般方案是使用zk或者redis,zk的特点是并发量差点。分为排他锁和共享锁。
l分布式队列:FIFO模型或者Barrier模型。
数据存储
Zookeeper采用了递归的HashMap来实现树形结构,对于每一个节点znode,都记录了父亲节点id、儿子节点ID集合、本节点访问权限(ACL)、本节点数据、本节点的基本信息stat。这样节点的id拼接起来就是数据key。节点数据直接使用byte存储,原则上不要太大,最好不要超过1M,这点相对于redis提供丰富的数据结构就弱一些了。
在持久化方面,zookeeper会定时将数据快照到硬盘,同时对于所有历史事务都会记录增量的txtlog,这样在启动的时候会先加载持久化数据快照,再增量获取txtlog来保障数据的完整度。
数据节点可以根据顺序特点和持久性分为:顺序、无序、持久和临时。对于持久节点的key是znode的路径path,对于临时节点的key则是客户端会话的sessionid。对于有序的节点,key上面会增加“-”这种来标志。
主要流程和操作
Zk的客户端与服务器时间通过长连接保持,服务端会固定周期向客户端发起活跃检测请求确认客户端的活跃情况。
对于客户端提供的功能方面,zk提供了对于节点的增加、删除、修改、查询、监听等操作。这些操作除了监听都是原子性的,zk会对所有操作进行编号,并按顺序执行。当节点发生改变时,所有监听该节点的客户端都将收到更新时间,再由客户端去读取该节点数据。
原始的zk提供的api中,并不提供持久的监听功能,每个监听事件在触发后就将消失,需要客户端重新注册监听。
在zk实现中,监听也是一个HashMap的keyvalue查找。长连接也是一个连接池。
分布式部署场景各实例的角色和数据一致性
在分布式场景,zk将实例分为了三种角色:leader、follower、observer。这几种角色在客户端看来都是一样的。Leader负责整体集群的管理、数据写操作的管理、各实例活跃的检测等工作,follower和Observer都负责为客户端提供数据读的操作,但相对于follower的observer不具备选举能力和成为leader的能力。Observer可以认为是只读key-value服务器,存在的价值是提供整体集群的QPS。
Zk的数据在多实例之间的一致性通过zab协议来保障的,即任何对于集群会产生变更的操作都需要50%以上的leader+follower同意才能生效。主要有数据写操作和leader选举工作。
对于数据写操作,所有的请求都会转到leader实例,再有leader实例通知所有的follower和observer进行更新,当超过50%的实例反馈给leader可以更新时,leader再二次通知所有节点进行更新。
对于leader选举操作,主要是从单实例到多实例场景以及leader实例与部分实例断开连接的场景。在无leader的实例集群中,所有实例状态为looking状态,并向集群所以节点申请自己为leader,申请的信息是(实例id,zxid)的键值,实例会接受zxid比自己大的或者zxid相等但实例id比自己大的节点作为自己的leader,最终超过半数实例认可的话实例将成为leader。