Zookeeper是一个开源的分布式应用程序协调系统, 是Google的Chubby的一个开源实现(两者有所不同), 他是集群的管理者, 监视着集群中各个节点的状态(源码中有使用Observer[观察者]模式), 并根据节点提交的反馈进行下一步合理操作。最后, zookeeper将简单易用的接口和性能高效, 功能稳定的系统提供给用户。
客户端的读取请求可以被集群中的任意一台机器处理, 如果读取请求在节点上注册了监听器, 此监听器也是有所连接的zookeeper机器来处理。对于写入请求, 这些请求会同时被发给其他zookeeper机器并且达成一致后, 请求才会返回成功。因此, 随着zookeeper集群机器增多, 读取请求的吞吐量会提高但是写请求的吞吐量会下降。
Zookeeper的有序性: 所有的更新都是全局有序的, 每个更新都有一个唯一的时间戳(zxid[Zookeeper Transaction Id]), 而读取请求只会相对于更新有序, 也就是读取请求的返回结果中会带有该Zookeeper最新的zxid。
文件系统
通知机制
Zookeeper提供一个多层级的节点命名空间(namespace节点名: znode)。这些节点都可以设置关联的数据, 而文件系统中只有文件节点可以存放数据而目录节点不行。
Zookeeper为了保证高吞吐量和低延迟, 在内存中维护了树状的目录结构, 该特性使得Zookeeper不能用于存放大量的数据, 每个节点的存放数据2上限为1M。
persistent - 持久化目录节点
persistent_sequential-持久化顺序编号目录节点
ephemeral - 临时目录节点
ephemeral_sequential - 临时顺序编号目录节点
客户端(client)会对某个znode建立一个watcher事件, 当该node发生变化时, 这些client会受到Zookeeper的通知, 然后client可以根据znode变化来做出业务上的改变等。
命名服务
配置管理
集群管理
分布式锁
队列管理
命名服务: 通过指定的名字来获取资源或服务的地址, 利用Zookeeper创建一个全局的路径, 即使唯一的路径, 这个路径就可以作为一个名字, 指向集群中的集群, 提供的服务的地址, 甚至可以是一个远程的对象etc......
程序分布式地部署在不同的机器上, 将程序的配置信息放在Zookeeper的znode下, 当有配置发生改变时, 也就是znode发生变化时, 可以通过改变Zookeeper集群中某个节点的内容, 利用watcher通知给各个客户端, 从而更改配置。
集群管理:
(1) 监测是否有机器退出和加入
(2) 选举master主节点
锁服务:
(1). 保持独占
(2) 控制时序
获取分布式锁时会在locker(注意: 这边的locker只是个指代)节点下创建临时顺序节点, 释放锁时会删除该临时节点。客户端调用createNode方法在locker下创建临时顺序节点, 然后调用getChildren("locker")来获取locker下面的所有子节点, 而此时不用设置任何Watcher。
客户端获取到所有的子节点路径之后, 如果发现自己创建的节点在所有创建的子节点序号最小, name就认为该客户端获取到了锁。如果发现自己创建的节点并非locker所有子节点中最小的,则说明自己还没有获取到锁, 此时客户端需要找到比自己小的那个节点, 然后对其调用exists()方法, 同时对其注册事件监听器。之后, 让这个被关注的节点删除, 则客户端的Watcher会收到相应通知, 此时再次判断自己创建的节点是否是locker子节点中序号最小的, 如果是则获取到了锁, 如果不是则重复执行以上步骤直到获取到比自己小的一个节点并注册监听。
队列类型:
(1) 同步队列
当一个队列的成员都聚齐时, 这个队列才可用, 否则要一直等待所有队员到达。
管理方式: 在约定目录下创建临时目录节点, 监听节点数目是否是我们要求的数目。
(2) FIFO(First In First Out)队列
Zookeeper 数据复制
作为一个为集群提供一致的数据的服务, zookeeper自然需要在所有机器间做数据复制。
数据复制的优点:
以客户端读写访问的透明度来区分数据复制集群系统可分为以下两类:
Zookeeper采用的是任意写(Write Any)。随着机器数量增加, 他的读吞吐能力和相应能力扩展性非常好, 但写吞吐能力会逐渐下降(so建立observer观察), 而相应能力则取决于具体实现方式, 延迟复制保持最终一致性还是立刻复制快速响应。
Zookeeper的核心是原子广播, 这个机制保证了各个server之间的同步。实现该机制的协议为Zab协议, 它有两种模式: 恢复模式(选主) 和 广播模式(同步)。
状态同步保证了leader 和 Server 具有相同的系统状态。
Zookeeper采用了递增的事务Id来标识, 所有的提议(proposal)都在被提出来的时候加上了zxid(
一个64位的数字
高32位是epoch, 用来标识leader是否发生改变, 如果有新的leader产生, epoch会自增
低32位用来递增计数
)
当新产生proposal的时候, 会依据数据库的两阶段过程, 首先会向其他的server发出事务执行请求, 如果超过半数的机器都能执行并且能够成功, 那么就会开始执行。
每个Server在工作过程中的三种状态:
looking: 当前Server不知道leader是谁, 正在搜寻
leading: 当前Server即为选举出来的leader
following: leader已经选举出来, 当前Server与之同步
当leader崩溃或者leader失去大多数的follower, Zookeeper进入恢复模式, 在该模式下会重新选举出一个新的leader, 让所有的Server都恢复到一个正确的状态。
Zookeeper的选举算法有两种:
(1) Zookeeper选主流程(basic paxos)
选举线程由当前Server发起选举的线程担任, 其主要功能是对投票结果进行统计, 并选出推荐的Server
选举线程首先向所有Server发起一次询问(包括自己)
选举线程收到回复后, 验证是否是自己发起的询问(验证zxid是否一致), 然后获取对方的myid, 并存储到当前询问对象列表中, 最后获取对方提议的leader相关信息(myid, zxid), 并将这些信息存储到当次选举的投票记录表中
收到所有Server回复之后, 就会计算出zxid最大的那个Server, 并将这个Server相关信息设置成下一次要投票的Server
线程将当前zxid最大的Server设置为当前Server要推荐的leader, 如果此时获胜的Server获得n/2 + 1的Server票数, 设置当前推荐的leader为获胜的Server, 将根据获胜的Server相关信息设置自己的状态。否则, 迭代这个流程, 直到leader被选举出来。
恢复模式下, 如果是刚从崩溃状态恢复的或者是刚启动的server还会从磁盘快照中恢复数据和会话信息,
zookeeper会记录事务日志并定期进行快照, 方便在恢复时进行状态恢复。
(2) Zookeeper选主流程(fast paxos)
在选举过程中, 某Server首先向所有Server提议自己要成为leader, 当其他Server收到提议之后,解决epoch 和 zxid的冲突, 并接受对方的提议, 然后想对方发送接收提议完成的消息, 重复这个流程直到选举出leader。
选举出leader后, Zookeeper就进入状态同步过程。
从系统调度的角度来看, 操作人员发送通知实际上是通过控制台改变某个节点的状态, 然后Zookeeper将这些变化发送给注册了这个节点的watcher的所有客户端。
对于执行情况汇报: 每个工作进程都在某个目录下创建一个临时节点。并携带工作的进度数据, 这样汇总的进程可以监控目录子节点的变化获得工作进度的实时的全局状况。
在分布式环境中, 有些业务逻辑只需要集群中的某一台机器进行执行, 其他的机器就可以共享这个结果, 这样可以大大减少重复计算, 提高性能。
Zookeeper集群推荐配置不少于3个服务器, 它需要保证当一个节点宕机时, 其他节点会继续提供服务。
如果是一个follower(从节点)宕机, 仍有多台服务器提供访问, 而Zookeeper上的数据是有多个副本的,数据不会丢失
如果是一个leader(主节点)宕机, Zookeeper会选举出新的leader
只要挂掉的服务器数量不到总数的一半, 集群仍能运行。
Zookeeper的负载均衡是可以调控的, 而nginx只是能调权重, 其他需要可控的都需要自己写插件
nginx的吞吐量比Zookeeper大很多, 技术选型应该由业务需求决定
Zookeeper watch机制
一个Watch事件是一个一次性的触发器, 当贝设置Watch的数据发生了改变的时候, 则服务器将这个改变发送给设置了Watch的客户端, 以便通知它们。
watch机制的特点:
一次性触发数据发生改变时, 一个watch event会被发送到client, 但是client只会收到一次这样的信息。
watcher event异步发送watcher的通知事件从server发送到client是异步的, 由于不同的客户端和服务器之间通过socket进行通信, 由于网络延迟或其他因素导致客户端在不通的时刻监听到事件, 由于Zookeeper本身提供了ordering guarantee(客户端监听事件后, 才会感知它所监视的znode发生了变化)。所以使用Zookeeper不能期望能够监控到节点每次的变化。Zookeeper只能保证最终, 而无法保证强一致性。
数据监视: Zookeeper有数据监视和子数据监视
注册watcher: getData, exists, getChildren
触发watcher: create, delete, setData
setData()会触发znode上设置的data watch(如果设置成功的话)。 一个成功的create操作会触发被创建的znode上的数据watch, 以及其父节点上的child watch。
一个成功的delete()操作将会同时触发一个znode的data watch 和 child watch(因为这样就没有子节点了), 同时也会触发其父节点的child watch
当一个客户端连接到一个新的服务器上时, watch将被任意会话事件触发。当与一个服务器失去连接的时候, 是无法接收到watch的。而当client重新连接时, 所有先前注册过的watch, 都会被重新注册(if needed)。只有在该状况下, watch可能丢失: 对于一个未创建的znode的exist watch, 如果在客户端断开连接期间被创建了, 并且随后在客户端连接上之前又删除了, 这种情况下, 这个watch事件可能会被丢失。
Watch是轻量级的, 其实就是本地JVM的Callback, 服务器端只是存了是否有设置了Watcher的boolean类型
原文:https://www.cnblogs.com/ronnieyuan/p/11545399.html