ZooKeeper学习笔记一

分布式应用程序

分布式应用程序可以通过在它们之间协调以完成特定的任务,快速且有效的方式在多个系统中的网络在给定时间(同时)运行
分布式应用程序有两部分,分别是:服务器和客户端应用程序。如下图所示:

分布式应用程序的优点

  1. 可靠性
  2. 可扩展性
  3. 透明性

Zookeeper在分布式中提供的服务

  1. 命名服务
  2. 配置管理
  3. 集群管理
  4. 节点领导者选举
  5. 锁定和同步服务
  6. 数据注册表

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部分组成:

  1. stat. 此为状态信息, 描述该znode的版本, 权限等信息;
  2. data. 与该znode关联的数据;
  3. 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),时间戳和数据长度组成。

  1. cZxid:节点创建时的对应的zxid时间戳
  2. ctime:节点创建时的时间戳
  3. mZxid:节点最新一次更新发生时的对应的zxid时间戳
  4. mtime:节点最新一次更新发生时的时间戳
  5. pZxid:用于添加或删除子节点的znode更改的事务ID
  6. cversion:其子节点的更新次数
  7. dataVersion:节点数据的更新次数
  8. aclVersion:节点ACL(授权信息)的更新次数
  9. ephemeralOwner:如果该节点为ephemeral节点, ephemeralOwner值表示与该节点绑定的session id:如果该节点不是ephemeral节点, ephemeralOwner值为0:至于什么是ephemeral节点
  10. dataLength:节点数据的字节数
  11. numChildren:子节点个数

zxid

什么是zxid呢?

  1. ZooKeeper状态的每一次改变, 都对应着一个递增的Transaction id, 该id称为zxid.
  2. Zxid长度为64位,低32位表示计数器(counter)和高32位的纪元号(epoch)。epoch为当前leader在成为leader的时候生成的,且保证会比前一个leader的epoch大
  3. 由于zxid的递增性质, 如果zxid1小于zxid2, 那么zxid1肯定先于zxid2发生. 
  4. 创建任意节点, 或者更新任意节点的数据, 或者删除任意节点, 都会导致Zookeeper状态发生改变, 从而导致zxid的值增加.
  5. 实际上当新的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特性

一致性保证

  1. 可靠提交(Reliable delivery) -如果一个事务 A 被一个server提交(committed)了,那么它最终一定会被所有的server提交
  2. 全局有序(Total order) - 假设有A、B两个事务,有一台server先执行A再执行B,那么可以保证所有server上A始终都被在B之前执行
  3. 因果有序(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位是个递增计数。

  1. leader从客户端收到一个写请求
  2. leader生成一个新的事务并为这个事务生成一个唯一的ZXID,
  3. leader将这个事务发送给所有的follows节点
  4. follower节点将收到的事务请求加入到历史队列(history queue)中,并发送ack给ack给leader
  5. 当leader收到大多数follower(超过法定数量)的ack消息,leader会发送commit请求
  6. 当follower收到commit请求时,会判断该事务的ZXID是不是比历史队列中的任何事务的ZXID都小,如果是则提交,如果不是则等待比它更小的事务的commit

恢复模式

恢复模式大致可以分为四个阶段

  1. 选举
  2. 发现
  3. 同步
  4. 广播

恢复过程的步骤大致可分为

  1. 当leader崩溃后,集群进入选举阶段,开始选举出潜在的新leader(一般为集群中拥有最大ZXID的节点)
  2. 进入发现阶段,follower与潜在的新leader进行沟通,如果发现超过法定人数的follower同意,则潜在的新leader将epoch加1,进入新的纪元。新的leader产生
  3. 集群间进行数据同步,保证集群中各个节点的事务一致
  4. 集群恢复到广播模式,开始接受客户端的写请求

当 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 的读写机制

  1. Zookeeper是一个由多个server组成的集群
  2. 一个leader,多个follower
  3. 每个server保存一份数据副本
  4. 全局数据一致
  5. 分布式读写
  6. 更新请求转发,由leader实施

Zookeeper节点数据操作流程

  1. 在Client向Follwer发出一个写的请求
  2. Follwer把请求发送给Leader
  3. Leader接收到以后开始发起投票并通知Follwer进行投票
  4. Follwer把投票结果发送给Leader
  5. Leader将结果汇总后如果需要写入,则开始写入同时把写入操作通知给Leader,然后commit;
  6. Follwer把请求结果返回给Client

Follower主要的四个功能

  1. 向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);
  2. 接收Leader消息并进行处理;
  3. 接收Client的请求,如果为写请求,发送给Leader进行投票;
  4. 返回Client结果。

来自Leader的消息

  1. PING消息: 心跳消息;
  2. PROPOSAL消息:Leader发起的提案,要求Follower投票;
  3. COMMIT消息:服务器端最新一次提案的信息;
  4. UPTODATE消息:表明同步完成;
  5. REVALIDATE消息:根据Leader的REVALIDATE结果,关闭待revalidate的session还是允许其接受消息;
  6. SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。

Observer作用

  1. Zookeeper需保证高可用和强一致性;
  2. 为了支持更多的客户端,需要增加更多Server;
  3. Server增多,投票阶段延迟增大,影响性能;
  4. 权衡伸缩性和高吞吐率,引入Observer
  5. Observer不参与投票;
  6. Observers接受客户端的连接,并将写请求转发给leader节点;
  7. 加入更多Observer节点,提高伸缩性,同时不影响吞吐率

leader选举流程

https://i-blog.csdnimg.cn/blog_migrate/0eb2172fc27b38a2a2da324528934831.png

https://i-blog.csdnimg.cn/blog_migrate/8698494dc89c98acbcc5d9524336cd4f.png

  当 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 事件通知、丰富的节点类型可以实现分布式应用的核心功能:

  1. 数据的发布、订阅(配置中心)
  2. 命名服务
  3. 分布式锁
  4. 分布式队列

数据的发布订阅

数据的发布订阅(Publish/Subscribe)系统:就是配置中心

  1. 目标:配置信息的集中管理和动态更新;
  2. 过程:发布者,将数据发布到 ZK 的上,订阅者进行订阅数据,达到动态获取数据的目的;

具体过程参考:

发布订阅中,2 种典型模式的对比:

Push 模式

  1. Server 端的消息队列:不考虑消息累积问题,维护简单,Server 只需维护一份消息队列即可
  2. Subscriber 端的消息队列:需维护一个消息缓存队列,实现生产者-消费者模型,维护复杂
  3. 实时性:消息的实时性好,Server 端无需在意 Subscriber 的消费速度
  4. 可靠性:要求 Server & Subscriber 都要保证消息队列的可靠性

Pull 模式

  1. Server 端的缓存队列:完整的生产者-消费者模型,可能出现消息累积,维护复杂
  2. Subscriber 端的消息队列:不需要维护,根据自己消费速度,自己行读取消息
  3. 实时性:消息实时性较弱,并且轮询会浪费 CPU 和网络资源
  4. 可靠性:只需要保证 Server 端消息队列的可靠性即可

而 ZK,发布订阅模型,采用推拉结合方式:

  1. 设置订阅:Client 在 Server 上注册监视点
  2. Server push:Server 事件触发时,告知 Client 有时间发生,但未告知详细发生的内容
  3. Client pull:Client 主动 pull 一次,查询具体的变化

推拉结合的方式,主要优点:轻量级的 push,分 2 阶段,设计简单,同时,支持丰富的业务场景。

命名服务

命名服务是分布式系统最基本的公共服务之一,被命名的实体,通常是:

  1. 集群中的机器
  2. 服务地址

命名服务的关键点:

  1. 全局唯一的名字

ZK 中,使用持久化的顺序节点,作为全局唯一 ID(全局唯一ID)。

运行模式

Zookeeper有两种运行模式:

  1. 独立模式(standalone mode)
  2. 复制模式(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

https://www.cnblogs.com/raphael5200/p/5285583.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值