当前位置:   article > 正文

一文入门ZooKeeper

一文入门ZooKeeper

简介

官网:https://zookeeper.apache.org/index.html
分布式服务协调组件,Google Chubby的开源实现。解决分布式应用中的以下问题:配置管理、命名服务(Naming Service)、集群管理、统一命名服务、状态同步。

用于解决分布式数据一致性问题,提供顺序一致性、原子性、单一视图、可靠性、实时性等:

  • 顺序一致性:客户端的更新顺序与他们被发送的顺序相一致;
  • 原子性:每一个Znode节点上的数据都具有原子操作的特性,读操作将获取与节点相关的数据,写操作将替换节点上的数据。更新操作要么全部成功,要么全部失败;
  • 单一试图:无论客户端连接到哪一个服务器,都可以看到相同的ZK视图;
  • 可靠性:一旦一个更新操作被应用,那么在客户端再次更新它之前,其值将不会被改变;
  • 实时性:在特定的一段时间内,系统的任何变更都将被客户端检测到;

安装

ZK安装分单机、伪集群、集群三种方式。伪集群指的是在一台物理机上运行多个ZK实例,每个实例使用不同的端口启动服务;所有节点都处于一个物理机,存在单点故障;但是具有集群安装部署模式的一些特性,常用于测试环境试用ZK新版本的新功能,或调试学习用。生产环境会使用真正的集群部署模式。

单机模式

下面仅仅只讲述各种不同系统下简单模式的安装。伪分布式和完全分布式模式的安装,不同的仅仅只是不同节点的配置文件的部分配置。

  1. 下载zookeeper-3.9.2
  2. 将文件解压到/opt目录下,创建两个文件夹:data和logs。
  3. 将解压后zookeeper-3.9.2文件夹下的zoo_sample.cfg文件拷贝一份命名为zoo.cfg,并添加data和log目录配置指向刚才创建的两个目录。2888端口号是ZK服务之间通信的端口;3888是ZK与其他应用程序通信的端口
  4. dataDir=/opt/zookeeper-3.9.2/data下创建myid文件并编辑,并在对应的IP的机器上输入对应的编号。如在ZK上,myid文件内容就是1。如果只在单点上进行安装配置,那么只有一个server.1。
  5. .bash_profile文件中增加ZK配置,随后使配置生效:source .bash_profile
export ZOOKEEPER_HOME=/opt/zookeeper-3.9.2
export PATH=$ ZOOKEEPER_HOME/bin:$PATH
  • 1
  • 2
  1. 关闭防火墙:
#停止防火墙
systemctl stop firewalld.service
#禁止防火墙开机启动
systemctl disable firewalld.service
  • 1
  • 2
  • 3
  • 4
  1. 启动测试:zkServer.sh start,看到Mode:standalone就表示启动成功。
  2. 关闭ZK: zkServer.sh stop
  3. 配置开机启动ZK,vim rc.local添加su – root -c '/opt/zookeeper-3.4.10/bin/zkServer.sh start',在centos7中,/etc/rc.local的权限被降低,需执行命令赋予其可执行权限:chmod +x /etc/rc.d/rc.local

配置

zoo.cfg配置文件:

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial synchronization phase can take
initLimit=10
# The number of ticks that can pass between sending a request and getting an acknowledgement
syncLimit=5
dataDir=/root/fmm/data/zookeeper
dataLogDir=xxx/zookeeper/server1/
clientPort=2181
server.1=10.20.20.76:2887:3887
server.2=10.20.20.239:2887:3887
server.3=10.20.21.27:2887:3887
minSessionTimeout=4000
maxSessionTimeout=100000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

参数说明:

  • tickTime:基本时间单元,代表一次心跳时间,以毫秒为单位,最小超时时间为两个心跳时间;
  • dataDir:数据目录,用于存放内存数据库快照和集群的myid文件;
  • dataLogDir:用于单独设置transaction log的目录,transaction log分离可以避免和普通log还有快照的竞争。
  • clientPort:客户端连接ZK端口;
  • initLimit:初始化连接时最长能忍受多少个心跳时间间隔数。设定允许所有Follower与leader进行连接并同步的时间,如果在设定的时间段内,半数以上的Follower未能完成同步,leader便会宣布放弃领导地位,进行另一次的领导选举。如果zk集群环境数量确实很大,同步数据的时间会变长,因此这种情况下可以适当调大该参数;
  • syncLimit:标识Leader与Follower之间发送消息,请求和应答时间长度,最长不能超过多少个tickTime的时间长度。允许Follower与Leader进行同步的时间,如果在设定的时间段内,Follower未完成同步,它将会被集群丢弃。所有关联到这个Follower的客户端将连接到另外一个Follower;
  • server.X=A:B:C:其中X是一个数字, 表示这是第几号server,要写在myid文件中,决定当前机器的id。A是该server所在的IP地址,B配置该server和集群中的Leader交换消息所使用的端口,C配置选举Leader时所使用的端口。如果electionAlg为0,则不需要第二个port。伪集群模式安转时,后面连着的2个端口3个server都不要一样,否则端口冲突。
  • electionAlg
    用于选举的实现的参数:1-LeaderElection,2-AuthFastLeaderElection,3-FastLeaderElection,ZK默认使用FastLeaderElection进行Leader选举。

数据模型

也叫文件系统。ZK中数据是以目录结构的形式存储的。其中的每一个存储数据的节点都叫做ZNode,每个ZNode都有一个唯一的路径标识。和目录结构类似,每一个节点都可以可有子节点(临时节点除外)。节点中可以存储数据和状态信息,每个ZNode上可以配置监视器(Watcher),用于监听节点中的数据变化。节点不支持部分读写,而是一次性完整读写。

ZNode节点的Stat结构体由字段:

  • czxid:创建节点的zxid
  • mzxid:对ZNode最近修改的zxid
  • pzxid:对ZNode节点孩子节点最近修改的zxid
  • ctime:以距离时间原点(epoch)的毫秒数表示的ZNode创建时间
  • mtime:以距离时间原点的毫秒数表示的ZNode最近修改时间
  • version:ZNode数据的修改次数
  • cversion:ZNode子节点修改次数
  • aversion:ZNode的ACL修改次数
  • ephemeralOwner:如果ZNode是临时节点,则指示节点所有者的会话ID;如果不是临时节点,则为零
  • dataLength:ZNode数据长度
  • numChildren:ZNode子节点个数

节点

即Znode,有四种类型,PERSISTENT(持久节点)、PERSISTENT_SEQUENTIAL(持久的连续节点)、EPHEMERAL(临时节点)、EPHEMERAL_SEQUENTIAL(临时的连续节点)。Znode的类型在创建时确定并且之后不能再修改。节点中除了可以存储数据,还包含状态信息。

临时节点

临时节点的生命周期和客户端会话绑定。如果客户端会话失效,即关闭连接,则节点自动被清除。临时节点不能有子节点。

持久节点

指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点;不会因创建该节点的客户端会话失效而消失。

临时顺序节点

与临时节点不同的是,创建的节点会自动加上编号。

持久顺序节点

和持久节点类型是一致的。在ZK中,每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的范围是整型的最大值。

特性

Watcher

即数据变更通知,ZK允许客户端向服务端的某个Znode注册一个Watcher监听,当服务端的一些指定事件触发这个Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据Watcher通知状态和事件类型做出业务上的改变。

工作机制:

  1. 客户端注册Watcher
  2. 服务端处理Watcher
  3. 客户端回调Watcher

Watcher特性:

  1. 一次性
    无论是服务端还是客户端,一旦一个Watcher被触发,ZK都会将其从相应的存储中移除。这样的设计有效的减轻服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。
  2. 客户端串行执行
    客户端Watcher回调的过程是一个串行同步的过程
  3. 轻量
    1. Watcher 通知非常简单,只会告诉客户端发生事件,而不会说明事件的具体内容。
    2. 客户端向服务端注册Watcher时,并不会把客户端真实的Watcher对象实体传递到服务端,仅仅是在客户端请求中使用boolean类型属性进行标记。
  4. watcher event异步发送:Watcher的通知事件从server发送到Client是异步的,这就存在一个问题,不同的客户端和服务器之间通过socket进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于ZK提供 ordering guarantee,即客户端监听事件后,才会感知它所监视ZNode发生变化。所以使用ZK不能期望能够监控到节点每次的变化。ZK只能保证最终的一致性,而无法保证强一致性。
  5. 注册watcher:getData、exists、getChildren
  6. 触发watcher:create、delete、setData
  7. 当一个客户端连接到一个新的服务器上时,watch将会被以任意会话事件触发。当与一个服务器失去连接的时候,是无法接收到watch的。而当Client重新连接时,如果需要的话,所有先前注册过的watch,都会被重新注册。通常这是完全透明的。只有在一个特殊情况下,watch可能会丢失:对于一个未创建的ZNode的exist watch,如果在客户端断开连接期间被创建,并且随后在客户端连接上之前又删除,这种情况下,这个watch事件可能会被丢失。

ACL

Access Control List,访问控制列表。每一个Znode节点都拥有一个ACL,该列表规定用户对节点的访问权限。ZK定义5种权限:

  • CREATE:创建子节点的权限。
  • READ:获取节点数据和子节点列表的权限。
  • WRITE:更新节点数据的权限。
  • DELETE:删除子节点的权限。
  • ADMIN:设置节点ACL的权限。

CREATE和DELETE都是针对子节点的权限控制。

Chroot

3.2.0版本后,添加Chroot特性,允许每个客户端为自己设置一个命名空间。如果一个客户端设置Chroot,那么该客户端对服务器的任何操作,都将会被限制在其自己的命名空间下。

通过设置Chroot,能够将一个客户端应用于ZK服务端的一颗子树相对应,在那些多个应用公用一个ZK集群的场景下,用于实现不同应用间的相互隔离。

角色

ZK集群中有以下角色:
领导者(Leader):负责进行投票的发起和决议,更新系统状态;
跟随者(Follower):接受客户端请求、并将请求转发到Leader,向客户端返回结果,在选主过程中参与投票;
观察者(observer):可以接受客户端连接,将写请求转发给Leader,但不参加投票过程,只同步Leader的状态,目的是为了扩展系统,提高读取速度;
客户端(Client):请求发起方。

一个ZK集群同一时刻只会有一个Leader,其他都是Follower或Observer。默认只有Leader和Follower两种角色,没有Observer角色。为了使用Observer模式,在任何想变成Observer的节点的配置文件中加入peerType=observer,并在所有Server的配置文件中,配置成observer模式的Server的那行配置追加:observer,例如:server.1:localhost:2888:3888:observer

在装有ZK的机器的终端执行zookeeper-server status可查看当前节点的角色(Leader or Follower)。
集群的所有机器通过一个Leader选举过程来选定一台被称为Leader的机器,Leader服务器为客户端提供读和写服务。

Follower和Observer都能提供读服务,不能提供写服务。区别在于,Observer机器不参与Leader选举过程,也不参与写操作的『过半写成功』策略,因此Observer可以在不影响写性能的情况下提升集群的读性能。

API和客户端

API是ZK原生提供的命令,而客户端是基于API加以封装,旨在简化操作。本文只考虑Java客户端。

API

ZK提供以下API,供Client操作ZNode和其中存储的数据:

  • create(path, data, flags):创建路径为path的ZNode,在其中存储data[]数据,flags可设置为Regular或Ephemeral,并可选打上sequential标志,父ZNode必须存在。
  • delete(path, version):删除相应path及version的ZNode;必须得确保没有子ZNode,否则删除失败;
  • exists(path,watch):如果存在path对应ZNode,则返回true;否则返回false,watch标志可设置监听事件
  • getData(path, watch):返回对应ZNode的数据和元信息(如version等)
  • setData(path, data, version):将data[]数据写入对应path和version的ZNode
  • getChildren(path, watch):返回指定ZNode的子节点集合
  • sync:使客户端的ZNode视图与ZK同步;
  • getACL/setACL:获取ZNode的ACL,或设置ACL;

更新ZK操作是有限制的。delete或setData必须明确要更新的Znode的版本号,通过exists找到。如果版本号不匹配,更新将会失败。更新ZK操作是非阻塞式的。因此客户端如果失去一个更新(由于另一个进程在同时更新这个Znode),他可以在不阻塞其他进程执行的情况下,选择重新尝试或进行其他操作。

尽管ZK可以被看做是一个文件系统,但是出于便利,摒弃一些文件系统地操作原语。因为文件非常的小并且使整体读写的,所以不需要打开、关闭或是寻地的操作。

Zookeeper

原生客户端。

Curator

另起一篇,参考Zookeeper系列之客户端Curator

ZkClient

<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.11</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

最后一次release是18年10月23日,不建议使用。

解决如下问题:

  • session会话超时重连
  • 解决Watcher反复注册
  • 简化API使用

ZkClient对原生API进行封装,但也有不足之处:

  • 几乎没有参考文档;
  • 异常处理简化(抛出RuntimeException);
  • 重试机制比较难用;
  • 没有提供各种使用场景的实现;

应用场景

分布式选举

使用ZK的分布式选举功能开源组件包括:

参考ZooKeeper系列之ZAB协议

分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。参考微服务系列之分布式锁

集群管理

Server启动时与ZK建立会话,创建EPHEMERAL节点,Server停止时,会话终止,EPHEMERAL节点被删除。

配置管理

配置信息,一般都是常数,如数据库连接池信息,通常具备以下3个特性。

  • 数据量通常比较小。
  • 数据内容在运行时动态变化。
  • 集群中各机器共享,配置一致。

推崇配置和代码分离。一般都是使用配置文件的方式,然后在代码中引入这些配置文件。早期,配置比较少,应用简单数量不多,修改配置重启应用,不是大问题。但是不适应互联网常规做法。常规做法是引入分布式配置中心,统一管理所有应用的配置文件,支持热修改。一般,不会改变的配置信息,可以放在代码中(在类私有或者类共有),或者配置文件中,变化频繁的配置信息放在配置中心。

一般有推拉两种模式:

  • 推:服务端主动将数据更新发送给所有订阅的客户端。
  • 拉:客户端主动发起请求来获取最新数据,通常客户端都采用定时轮询拉取的方式。

推拉结合是值得推荐的配置生效方式。ZK采用的是推拉相结合的方式。客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知后,需要主动到服务端获取最新的数据(推拉结合)。

诸多服务的正常运行都依赖这个配置中心,所以需要保证很高的可靠性,即高可用。故而配置中心一般用集群来保证可靠性,那如何保证配置在集群中的一致性呢?

ZK使用ZAB一致性协议来提供一致性。HBase中,客户端就是连接一个ZK,获得必要的HBase集群的配置信息,然后才可以进一步操作。消息队列Kafka中,也使用ZK来维护broker的信息。Dubbo中也广泛的使用ZK管理一些配置来实现服务治理。

命名服务

即Naming Service,在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的机器,提供的服务地址列表,远程对象等——这些都可以统称为名字(Name)。

通过在ZK里创建顺序节点,能够很容易创建一个全局唯一的路径,这个路径就可以作为一个名字。ZK的命名服务即生成全局唯一的ID。

分布式队列

可以实现两种类型的队列:

  1. 同步队列:当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。在指定目录下创建临时目录节点,监听节点数目是否是要求的数目。
  2. 队列按照 FIFO 方式进行入队和出队操作。和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。

心跳检测

机器间的心跳检测机制是指在分布式环境中,不同机器(或进程)之间需要检测到彼此是否在正常运行。在传统的开发中,通常是通过主机直接是否可以相互PING通来判断,更复杂一点的话,则会通过在机器之间建立长连接,通过TCP连接固有的心跳检测机制来实现上层机器的心跳检测,这些都是非常常见的心跳检测方法。

基于ZK的临时节点的特性,可以让不同的进程都在ZK的一个指定节点下创建临时子节点,不同的进程直接可以根据这个临时子节点来判断对应的进程是否存活。通过这种方式,检测和被检测系统直接并不需要直接相关联,而是通过ZK上的某个节点进行关联,大大减少系统耦合。

工具

客户端

可视化

监控

原生监控命令

ZK提供一些4字命令,用于获得ZK集群中,某台ZK的角色、ZNode数、健康状态等信息;

  • mntr:显示自身角色、ZNode数、平均调用耗时、收包发包数等信息
  • ruok:诊断自身状态是否ok,不一定准确。
  • cons:展示当前的Client连接

通过ZK自带的zkCli.sh模拟Client创建ZNode:
/usr/local/zookeeper/bin/zkCli.sh create /zookeeper/test 'test' >/dev/null 2>&1

删除ZNode:
/usr/local/zookeeper/bin/zkCli.sh delete /zookeeper/test >/dev/null 2>&1

再根据返回值判断添加、删除ZNode是否成功,从而判断该台ZK状态是否正常。

三方监控工具

对比

对比Chubby

Google Chubby。ZooKeeper参考Chubby的开源实现版本。

对比Paxos

参考

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/606628
推荐阅读
相关标签
  

闽ICP备14008679号