ZooKeeper学习笔记一
分布式应用程序
分布式应用程序可以通过在它们之间协调以完成特定的任务,快速且有效的方式在多个系统中的网络在给定时间(同时)运行
分布式应用程序有两部分,分别是:服务器和客户端应用程序。如下图所示:
分布式应用程序的优点
- 可靠性
- 可扩展性
- 透明性
Zookeeper在分布式中提供的服务
- 命名服务
- 配置管理
- 集群管理
- 节点领导者选举
- 锁定和同步服务
- 数据注册表
ZooKeeper是什么
ZooKeeper是一个高可用的分布式数据管理与系统协调框架。
基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性,也正是基于这样的特性,使得ZooKeeper解决很多分布式问题。
ZooKeeper的特性?
Zookeeper是非常简单和高效的。因为它的目标就是,作为建设复杂服务的基础,比如同步。zookeeper提供了一套保证,他们包括:
- 最终一致性:client不论连接到那个Server,展示给它的都是同一个视图。
- 可靠性:具有简单、健壮、良好的性能、如果消息m被到一台服务器接收,那么消息m将被所有服务器接收。
- 实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。
- 等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。
- 原子性:更新只能成功或者失败,没有中间状态。
- 顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。
ZooKeeper基础
ZooKeeper的体系结构
描绘ZooKeeper 的“客户端 – 服务器架构,如下图所示
zk角色
部分 |
描述 |
Client(客户端) |
客户端,我们的分布式应用集群中的一个节点,从服务器访问信息。对于特定的时间间隔,每个客户端向服务器发送消息以使服务器知道客户端是活跃的。 类似地,当客户端连接时,服务器发送确认码。如果连接的服务器没有响应,客户端会自动将消息重定向到另一个服务器。 |
Server(服务器) |
服务器,我们的ZooKeeper总体中的一个节点,为客户端提供所有的服务。向客户端发送确认码以告知服务器是活跃的。 |
Ensemble |
ZooKeeper服务器组。形成ensemble所需的最小节点数为3。 |
Leader |
服务器节点,如果任何连接的节点失败,则执行自动恢复。Leader在服务启动时被选举。 |
Follower |
跟随leader指令的服务器节点。 |
zk节点(分层命名空间)
下图显示了用于内存中表示 ZooKeeper 文件系统的树形结构。
ZooKeeper节点被称为znode。每个znode由一个名称识别,并通过路径(/)序列隔开。
图中的每个节点称为一个znode. 每个znode由3部分组成:
- stat. 此为状态信息, 描述该znode的版本, 权限等信息;
- data. 与该znode关联的数据;
- children. 该znode下的子节点;
zookeeper名字空间由节点znode构成,其组织方式类似文件系统,其中各个节点相当于目录和文件,通过路径作为唯一标识。
与文件系统不同的是,每个节点具有与之对应的数据内容,同时也可以具有子节点。
在 ZooKeeper 数据模型中每个 znode 维护一个 stat 结构。
Znode的类型
Znode被分为持久(persistent)节点,顺序(sequential)节点和临时(ephemeral)节点。
- 持久节点 - 即使在创建该特定znode的客户端断开连接后,持久节点仍然存在。默认情况下,除非另有说明,否则所有znode都是持久的。
- 临时节点 - 客户端活跃时,临时节点就是有效的。当客户端与ZooKeeper集合断开连接时,临时节点会自动删除。因此,只有临时节点不允许有子节点。如果临时节点被删除,则下一个合适的节点将填充其位置。临时节点在leader选举中起着重要作用。
- 顺序节点 - 顺序节点可以是持久的或临时的。当一个新的znode被创建为一个顺序节点时,ZooKeeper通过将10位的***附加到原始名称来设置znode的路径。例如,如果将具有路径 /myapp 的znode创建为顺序节点,则ZooKeeper会将路径更改为 /myapp0000000001 ,并将下一个***设置为0000000002。如果两个顺序节点是同时创建的,那么ZooKeeper不会对每个znode使用相同的数字。顺序节点在锁定和同步中起重要作用。
Stat(znode状态信息)
使用get命令获取指定节点的数据时, 同时也将返回该节点的状态信息, 称为Stat. 其包含如下字段:
ZooKeeper数据模型中的每个znode都维护着一个 stat 结构。一个stat仅提供一个znode的元数据。它由版本号,操作控制列表(ACL),时间戳和数据长度组成。
- cZxid:节点创建时的对应的zxid时间戳
- ctime:节点创建时的时间戳
- mZxid:节点最新一次更新发生时的对应的zxid时间戳
- mtime:节点最新一次更新发生时的时间戳
- pZxid:用于添加或删除子节点的znode更改的事务ID
- cversion:其子节点的更新次数
- dataVersion:节点数据的更新次数
- aclVersion:节点ACL(授权信息)的更新次数
- ephemeralOwner:如果该节点为ephemeral节点, ephemeralOwner值表示与该节点绑定的session id:如果该节点不是ephemeral节点, ephemeralOwner值为0:至于什么是ephemeral节点
- dataLength:节点数据的字节数
- numChildren:子节点个数
zxid
什么是zxid呢?
- ZooKeeper状态的每一次改变, 都对应着一个递增的Transaction id, 该id称为zxid.
- Zxid长度为64位,低32位表示计数器(counter)和高32位的纪元号(epoch)。epoch为当前leader在成为leader的时候生成的,且保证会比前一个leader的epoch大
- 由于zxid的递增性质, 如果zxid1小于zxid2, 那么zxid1肯定先于zxid2发生.
- 创建任意节点, 或者更新任意节点的数据, 或者删除任意节点, 都会导致Zookeeper状态发生改变, 从而导致zxid的值增加.
- 实际上当新的leader选举成功后,会拿到当前集群中最大的一个ZXID,并去除这个ZXID的epoch,并将此epoch进行加1操作,作为自己的epoch。
Sessions(会话)
在client和server通信之前, 首先需要建立连接, 该连接称为session. 连接建立后, 如果发生连接超时, 授权失败, 或者显式关闭连接, 连接便处于CLOSED状态, 此时session结束.
会话对于ZooKeeper的操作非常重要。会话中的请求按FIFO顺序执行。一旦客户端连接到服务器,将建立会话并向客户端分配会话ID 。
客户端以特定的时间间隔发送心跳以保持会话有效。如果ZooKeeper集合在超过服务器开启时指定的期间(会话超时)都没有从客户端接收到心跳,则它会判定客户端死机。
会话超时通常以毫秒为单位。当会话由于任何原因结束时,在该会话期间创建的临时节点也会被删除。
Zookeeper会为每个client分配一个session,类似于web服务器一样。针对session可以有保存一些关联数据
- Global session 全局Session,在每个server上都存在
- Local session 只在当前请求的server上存在,但只能进行读操作,要是要进行写操作,就得升级为全局session
Watches(监视)
监视是一种简单的机制,使客户端收到关于ZooKeeper集合中的更改的通知。客户端可以在读取特定znode时设置Watches。Watches会向注册的客户端发送任何znode(客户端注册表)更改的通知。
Znode更改是与znode相关的数据的修改或znode的子项中的更改。只触发一次watches。如果客户端想要再次通知,则必须通过另一个读取操作来完成。当连接会话过期时,客户端将与服务器断开连接,相关的watches也将被删除。
Zookeeper的工作原理
Zookeeper的核心是原子广播,这个机制保证了各个server之间的同步。
实现这个机制的协议叫做Zab协议。
Zab协议有两种模式,它们分别是恢复模式和广播模式。
当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server的完成了和leader的状态同步以后,恢复模式就结束了。
状态同步保证了leader和server具有相同的系统状态
ZAB协议介绍
在做分布式系统时,我们常常需要维护管理集群的配置信息、服务的注册发现、共享锁等功能,而ZooKeeper正是解决这些问题的一把好手。ZAB(ZooKeeper Atomic Broadcast)则是为ZooKeeper设计的一种支持崩溃恢复的原子广播协议。
很多人会误以为ZAB协议是Paxos的一种特殊实现,事实上他们是两种不同的协议。ZAB和Paxos最大的不同是,ZAB主要是为分布式主备系统设计的,而Paxos的实现是一致性状态机(state machine replication)
尽管ZAB不是Paxos的实现,但是ZAB也参考了一些Paxos的一些设计思想,比如:
- leader向follows提出提案(proposal)
- leader 需要在达到法定数量(半数以上)的follows确认之后才会进行commit
- 每一个proposal都有一个纪元(epoch)号,类似于Paxos中的选票(ballot)
ZAB特性
一致性保证
- 可靠提交(Reliable delivery) -如果一个事务 A 被一个server提交(committed)了,那么它最终一定会被所有的server提交
- 全局有序(Total order) - 假设有A、B两个事务,有一台server先执行A再执行B,那么可以保证所有server上A始终都被在B之前执行
- 因果有序(Causal order) - 如果发送者在事务A提交之后再发送B,那么B必将在A之前执行
只要大多数(法定数量)节点启动,系统就行正常运行
当节点下线后重启,它必须保证能恢复到当前正在执行的事务
ZAB的具体实现
- ZooKeeper由client、server两部分构成
- Client可以在任何一个server节点上进行读操作
- Client可以在任何一个server节点上发起写请求,非leader节点会把此次写请求转发到leader节点上。由leader节点执行
- ZooKeeper使用改编的两阶段提交协议来保证server节点的事务一致性
历史队列(history queue)
每一个follower节点都会有一个先进先出(FIFO)的队列用来存放收到的事务请求,保证执行事务的顺序
可靠提交由ZAB的事务一致性协议保证 全局有序由TCP协议保证 因果有序由follower的历史队列(history queue)保证
ZAB工作模式
- 广播(broadcast)模式
- 恢复(recovery)模式
广播(broadcast)模式
一旦leader已经和多数的follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,
发现leader,并和leader进行状态同步。待到同步结束,它也参与消息广播。Zookeeper服务一直维持在Broadcast状态,直到leader崩溃了或者leader失去了大部分的followers支持。
广播模式需要保证proposal被按顺序处理,因此zk采用了递增的事务id号(zxid)来保证。所有的提议(proposal)都在被提出的时候加上了zxid。
实现中zxid是一个64为的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch。低32位是个递增计数。
- leader从客户端收到一个写请求
- leader生成一个新的事务并为这个事务生成一个唯一的ZXID,
- leader将这个事务发送给所有的follows节点
- follower节点将收到的事务请求加入到历史队列(history queue)中,并发送ack给ack给leader
- 当leader收到大多数follower(超过法定数量)的ack消息,leader会发送commit请求
- 当follower收到commit请求时,会判断该事务的ZXID是不是比历史队列中的任何事务的ZXID都小,如果是则提交,如果不是则等待比它更小的事务的commit
恢复模式
恢复模式大致可以分为四个阶段
- 选举
- 发现
- 同步
- 广播
恢复过程的步骤大致可分为
- 当leader崩溃后,集群进入选举阶段,开始选举出潜在的新leader(一般为集群中拥有最大ZXID的节点)
- 进入发现阶段,follower与潜在的新leader进行沟通,如果发现超过法定人数的follower同意,则潜在的新leader将epoch加1,进入新的纪元。新的leader产生
- 集群间进行数据同步,保证集群中各个节点的事务一致
- 集群恢复到广播模式,开始接受客户端的写请求
当 leader在commit之后但在发出commit消息之前宕机,即只有老leader自己commit了,而其它follower都没有收到commit消息 新的leader也必须保证这个proposal被提交.(新的leader会重新发送该proprosal的commit消息)
当 leader产生某个proprosal之后但在发出消息之前宕机,即只有老leader自己有这个proproal,当老的leader重启后(此时左右follower),新的leader必须保证老的leader必须丢弃这个proprosal.(新的leader会通知上线后的老leader截断其epoch对应的最后一个commit的位置)
Zookeeper 的读写机制
- Zookeeper是一个由多个server组成的集群
- 一个leader,多个follower
- 每个server保存一份数据副本
- 全局数据一致
- 分布式读写
- 更新请求转发,由leader实施
Zookeeper节点数据操作流程
- 在Client向Follwer发出一个写的请求
- Follwer把请求发送给Leader
- Leader接收到以后开始发起投票并通知Follwer进行投票
- Follwer把投票结果发送给Leader
- Leader将结果汇总后如果需要写入,则开始写入同时把写入操作通知给Leader,然后commit;
- Follwer把请求结果返回给Client
Follower主要的四个功能
- 向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);
- 接收Leader消息并进行处理;
- 接收Client的请求,如果为写请求,发送给Leader进行投票;
- 返回Client结果。
来自Leader的消息
- PING消息: 心跳消息;
- PROPOSAL消息:Leader发起的提案,要求Follower投票;
- COMMIT消息:服务器端最新一次提案的信息;
- UPTODATE消息:表明同步完成;
- REVALIDATE消息:根据Leader的REVALIDATE结果,关闭待revalidate的session还是允许其接受消息;
- SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。
Observer作用
- Zookeeper需保证高可用和强一致性;
- 为了支持更多的客户端,需要增加更多Server;
- Server增多,投票阶段延迟增大,影响性能;
- 权衡伸缩性和高吞吐率,引入Observer
- Observer不参与投票;
- Observers接受客户端的连接,并将写请求转发给leader节点;
- 加入更多Observer节点,提高伸缩性,同时不影响吞吐率
leader选举流程
当 leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的 Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。
basic paxos流程:
1 .选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;
2 .选举线程首先向所有Server发起一次询问(包括自己);
3 .选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;
4. 收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个Server相关信息设置成下一次要投票的Server;
5. 线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得n/2 + 1的Server票数, 设置当前推荐的leader为获胜的Server,将根据获胜的Server相关信息设置自己的状态,否则,继续这个过程,直到leader被选举出来。
通 过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于 n+1.每个Server启动后都会重复以上流程。在恢复模式下,如果是刚从崩溃状态恢复的或者刚启动的server还会从磁盘快照中恢复数据和会话信 息,zk会记录事务日志并定期进行快照,方便在恢复时进行状态恢复。
使用场景
ZK 基于 Watcher 事件通知、丰富的节点类型可以实现分布式应用的核心功能:
- 数据的发布、订阅(配置中心)
- 命名服务
- 分布式锁
- 分布式队列
数据的发布订阅
数据的发布订阅(Publish/Subscribe)系统:就是配置中心
- 目标:配置信息的集中管理和动态更新;
- 过程:发布者,将数据发布到 ZK 的上,订阅者进行订阅数据,达到动态获取数据的目的;
具体过程参考:
发布订阅中,2 种典型模式的对比:
Push 模式
- Server 端的消息队列:不考虑消息累积问题,维护简单,Server 只需维护一份消息队列即可
- Subscriber 端的消息队列:需维护一个消息缓存队列,实现生产者-消费者模型,维护复杂
- 实时性:消息的实时性好,Server 端无需在意 Subscriber 的消费速度
- 可靠性:要求 Server & Subscriber 都要保证消息队列的可靠性
Pull 模式
- Server 端的缓存队列:完整的生产者-消费者模型,可能出现消息累积,维护复杂
- Subscriber 端的消息队列:不需要维护,根据自己消费速度,自己行读取消息
- 实时性:消息实时性较弱,并且轮询会浪费 CPU 和网络资源
- 可靠性:只需要保证 Server 端消息队列的可靠性即可
而 ZK,发布订阅模型,采用推拉结合方式:
- 设置订阅:Client 在 Server 上注册监视点
- Server push:Server 事件触发时,告知 Client 有时间发生,但未告知详细发生的内容
- Client pull:Client 主动 pull 一次,查询具体的变化
推拉结合的方式,主要优点:轻量级的 push,分 2 阶段,设计简单,同时,支持丰富的业务场景。
命名服务
命名服务是分布式系统最基本的公共服务之一,被命名的实体,通常是:
- 集群中的机器
- 服务地址
命名服务的关键点:
- 全局唯一的名字
ZK 中,使用持久化的顺序节点,作为全局唯一 ID(全局唯一ID)。
运行模式
Zookeeper有两种运行模式:
- 独立模式(standalone mode)
- 复制模式(replicated mode)
独立模式
只有一个zookeeper服务实例,不可保证高可靠性和恢复性,可在测试环境中使用,生产环境不建议使用。
复制模式
复制模式也就是集群模式,有多个zookeeper实例在运行,建议多个zk实例是在不同的服务器上。
集群中不同zookeeper实例之间数据不停的同步。
有半数以上的实例保持正常运行,zk服务就能正常运行。假如有半数及以上实例挂了,zk服务就不能正常运行。
例如:有5个zk实例,挂了2个,还剩3个,依然可以正常工作;如有6个zk实例,挂了3个(及以上),则不能正常工作。
每个znode的修改都会被复制到超过半数的机器上,这样就会保证至少有一台机器会保存最新的状态,其余的副本最终都会跟新到这个状态。Zookeeper为实现这个功能,使用了Zab协议,该协议有两个可以无限重复的阶段:
选举领导
同上
原子广播
上边已经说到:所有的写操作都会转发给leader,然后leader会将更新广播给所有的follower,当半数以上的实例都完成写操作后,leader才会提交这个写操作,随后客户端会收到写操作执行成功的响应。这么来的话,就实现了客户端的写操作的原子性,每个写操作要么成功要么失败。逻辑和数据库的两阶段提交协议很像。
复制模式下的数据一致性
Znode的每次写操作都相当于数据库里的一次事务提交,每个写操作都有个全局唯一的ID,称为:zxid(ZooKeeper Transaction)。ZooKeeper会根据写操作的zxid大小来对操作进行排序,zxid小的操作会先执行。zk下边的这些特性保证了它的数据一致性:
顺序一致性
任意客户端的写操作都会按其发送的顺序被提交。如果一个客户端把某znode的值改为a,然后又把值改为b(后面没有其它任何修改),那么任何客户端在读到值为b之后都不会再读到a。
原子性
这一点再前面已经说了,写操作只有成功和失败两种状态。
单一系统映像
客户端只会连接host列表中状态最新的那些实例。如果正在连接到的实例挂了,客户端会尝试重新连接到集群中的其他实例,那么此时滞后于故障实例的其它实例都不会接收该连接请求,只有和故障实例版本相同或更新的实例才接收该连接请求。
持久性
写操作完成之后将会被持久化存储,不受服务器故障影响。
及时性
在对某个znode进行读操作时,应该先执行sync方法,使得读操作的连接所连的zk实例能与leader进行同步,从而能读到最新的类容。
注意:sync调用是异步的,无需等待调用的返回,zk服务器会保证所有后续的操作会在sync操作完成之后才执行,哪怕这些操作是在执行sync之前被提交的。
参考:
https://www.cnblogs.com/shengkejava/p/5611671.html
https://www.cnblogs.com/leocook/p/zk_1.html
https://www.zhihu.com/question/35139415
http://ningg.top/zookeeper-lesson-11-zookeeper-application/
http://www.cnblogs.com/leesf456/p/6036548.html
https://www.cnblogs.com/lanqiu5ge/p/9405601.html
https://mp.weixin.qq.com/s/AE_2U5tUjEZSYoLovlcU6g
https://www.w3cschool.cn/zookeeper/zookeeper_fundamentals.html