分布式应用程序协调服务 ZooKeeper

1、简介:

  ZooKeeper 是一个分布的、开源的协调服务,它主要是用来解决分布式应用中经常遇到的一些数据管理问题。统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等,简化分布式应用协调及其管理的难度,提供高性能的分布式服务。

2、ZooKeeper 目标:

  封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。

3、ZooKeeper 工作方式:

  本身可以以 Standalone 模式安装运行,不过它的长处在于通过分布式 ZooKeeper 集群(一个Leader,多个 Follower),基于一定的策略来保证 ZooKeeper 集群的稳定性和可用性,从而实现分布式应用的可靠性。

  最新的版本可以在官网 http://zookeeper.apache.org/releases.html 来下载 zookeeper 的最新版本。

4、ZooKeeper 特点:

  最终一致性:为客户端展示同一个视图,这是 ZooKeeper 里面一个非常重要的功能。
  可靠性:如果消息被一台服务器接受,那么它将被所有的服务器接受。
  实时性:ZooKeeper 不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用 sync() 接口。
  独立性:各个 Client 之间互不干预。
  原子性:更新只能成功或者失败,没有中间状态。
  顺序性:所有 Server,同一消息发布顺序一致。

5、ZooKeeper 工作原理:

  每个 Server 在内存中存储了一份数据;
  ZooKeeper 启动时,将从实例中选举一个 Leader(Paxos 协议);
  Leader 负责处理数据更新等操作(Zab 协议);
  一个更新操作成功,当且仅当大多数 Server 在内存中成功修改数据。

  选举机制:

    全新集群选举:每个机器都给自己投票、投票数过半选举结束。myid 大的权重大。
    非全新集群选举:数据ID、服务器ID和逻辑时钟
    逻辑时钟小的选举结果被忽略,重新投票;
    统一逻辑时钟后,数据 ID 大的胜出;
    数据 ID 相同,服务器 ID 大的胜出。

  Paxos 协议:

    它是一个基于消息传递的一致性算法。Paxos 有一个前提:没有拜占庭将军问题。就是说 Paxos 只有在一个可信的计算环境中才能成立,这个环境是不会被入侵所破坏的。

    工作流程:提出提议,议员超过半数则提议生效,通知所有。顺序性提议编号。

  ZAB 协议:

  用来保障分布式数据一致性。ZAB 是一种支持崩溃恢复的消息广播协议,采用类似 2PC 的广播模式保证正常运行时性能,并使用基于 Paxos 的策略保证崩溃恢复时的一致性。

  ZAB 协议中节点存在四种状态:
    Leading:当前节点为集群 Leader,负责协调事务
    Following:当前节点为 Follower 在 Leader 协调下执行事务
    Looking:集群没有正在运行的 Leader, 正处于选举过程
    Observing:节点跟随 Leader 保存系统最新的状态提供读服务,但不参与选举和事务投票

  ZAB 协议的两种工作模式:
    广播模式:当集群正常运行过程中,Leader 使用广播模式保证各 Follower 节点的一致性
    恢复模式:集群启动或 Leader 崩溃时系统进入恢复模式,选举 Leader 并将集群中各节点的数据同步到最新状态

  ZooKeeper 集群中每个节点都会存储系统数据的完整副本,可以独立处理读请求。
  当 Follower 收到写请求时会将其转发给 Leader, Leader 为每个写请求分配唯一的全局有序的事务ID(Zookeeper Transaction Id, ZXID)。
  Leader 在广播模式下协调各 Follower 完成事务,并保证集群更新到一致的状态。

6、ZooKeeper 角色:

  领导者(Leader):领导者负责进行投票的发起和决议,更新系统状态,处理写请求。
  跟随者(Follwer):Follower 用于接收客户端的读写请求并向客户端返回结果,在选主过程中参与投票。
  观察者(Observer):观察者可以接收客户端的读写请求,并将写请求转发给 Leader,但 Observer节点不参与投票过程,只同步 leader 状态,Observer 的目的是为了扩展系统,提高读取速度。
  客户端(Client):执行读写请求的发起方。

7、ZooKeeper 的数据模型:

  树形结构的命名空间,与文件系统类似,层次化的目录结构,命名符合常规文件系统规范;
  数据大小不超过 1MB(可配置),数据读写要保证完整性;
  每个节点在 zookeeper 中叫做 znode,并且其有一个唯一的路径标识;
  节点(znode)都可以存数据,可以有子节点,节点不支持重命名;
  节点 Znode 可以包含数据和子节点(临时节点不能有子节点);
  Znode 中的数据可以有多个版本,比如某一个路径下存有多个数据版本,那么查询这个路径下的数据需带上版本;
  客户端应用可以在节点上设置监视器(Watcher)
  节点不支持部分读写,而是一次性完整读写;
  Znode 有两种类型,短暂的(ephemeral)和持久的(persistent)
  Znode 的类型在创建时确定并且之后不能再修改;
  短暂 znode 的客户端会话结束时,zookeeper 会将该短暂 znode 删除,短暂 znode 不可以有子节点;
  持久 znode 不依赖于客户端会话,只有当客户端明确要删除该持久 znode 时才会被删除;
  Znode 有 四 种 形 式 的 目 录 节 点 ,PERSISTENT、PERSISTENT_SEQUENTIAL、EPHEMERAL、EPHEMERAL_SEQUENTIAL。

  节点属性:
    dataVersion(数据版本号)
    cversion(子节点版本号)
    ACLVersion
    cZxid(创建的事务id)
    mZxid(修改的事务id)
    ctime(节点创建时间)
    mtime(节点更新时间)
    ephemeralOwner(0x0:永久节点)

8、数据发布与订阅:

  创建数据目录,订阅者初次启动的时候去 ZK 指定的节点获取相关的订阅信息;获取数据的同时,设置监听(监听节点数据的变化);一旦节点数据改变,触发监听,订阅者收到事件通知,获取数据,再次设置监听。
  注:统一管理的数据不能太大

9、ZooKeeper 主要应用场景:

  (1)统一命名服务:

    分布式环境下,经常需要对应用/服务进行统一命名,便于识别不同服务;
    类似于域名与 ip 之间对应关系,域名容易记住;
    通过名称来获取资源或服务的地址,提供者等信息;
    按照层次结构组织服务/应用名称;
    可将服务名称以及地址信息写到 ZooKeeper 上,客户端通过 ZooKeeper 获取可用服务列表类。

  (2)配置管理服务:

    分布式环境下,配置文件管理和同步是一个常见问题;
    一个集群中,所有节点的配置信息是一致的;
    对配置文件修改后,希望能够快速同步到各个节点上;
    配置管理可交由 ZooKeeper 实现,可将配置信息写入 ZooKeeper 的一个 znode 上,各个节点监听这个 znode ,一旦 znode 中的数据被修改,ZooKeeper 将通知各个节点。

  (3)集群管理:

    分布式环境中,实时掌握每个节点的状态是必要的;
    可根据节点实时状态作出一些调整;
    可交由 ZooKeeper 实现,可将节点信息写入 ZooKeeper 的一个 znode 上,监听这个 znode 可获取它的实时状态变化。

    实现思路:
    在 ZooKeeper 上创建一个临时节点,然后每个 Server 在它们创建目录节点的父目录节点上调用 getChildren(String path, boolean watch) 方法并设置 watch 为 true,由于是临时节点,当创建它的 Server 死去,这个目录节点也随之被删除,所以 Children 将会变化,这时 getChildren 上的 Watch 将会被调用,所以其它 Server 就知道已经有某台 Server 死去了。新增 Server 也是同样的原理。

  (4)分布式通知和协调:

    分布式环境中,经常存在一个服务需要知道它所管理的子服务的状态;
    NameNode 须知道各 DataNode 的状态
    JobTracker 须知道各 TaskTracker 的状态
    心跳检测机制可通过 ZooKeeper 实现,信息推送可由 ZooKeeper 实现(发布/订阅模式)

  (5)分布式锁:保持独占、控制时序(序列化)

    Client 首先在 ZK 上指定的目录创建节点(节点是一个非序列化的临时节点),只能有一个创建成功,谁创建成功,谁就获得访问数据文件的权限,操作完成断开和 ZK 的连接,其他应用如果需要操作这个文件,就去监听这个目录是否存在。

  (6)分布式队列:

    两种队列:
    当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。
    队列按照 FIFO 先进先出方式进行入队和出队操作,例如实现生产者和消费者模型。(可通过分布式锁实现)

    同步队列:
    一个 job 由多个 task 组成,只有所有任务完成后,job 才运行完成。
    可为 job 创建一个/job 目录,然后在该目录下,为每个完成的 task 创建一个临时 znode,一旦临时节点数目达到 task 总数,则 job 运行完成。
    同步队列实现思路:
    创建一个父目录 /synchronizing,每个成员都监控标志(Set Watch)位目录 /synchronizing/start 是否存在,然后每个成员都加入这个队列,加入队列的方式就是创建 /synchronizing/member_i 的临时目录节点,然后每个成员获取 / synchronizing 目录的所有目录节点,也就是 member_i。判断 i 的值是否已经是成员的个数,如果小于成员个数等待 /synchronizing/start 的出现,如果已经相等就创建/synchronizing/start。

    FIFO 队列实现思路:
    实现的思路也非常简单,就是在特定的目录下创建 SEQUENTIAL 类型的子目录 /queue_i,这样就能保证所有成员加入队列时都是有编号的,出队列时通过 getChildren( ) 方法可以返回当前所有的队列中的元素,然后消费其中最小的一个,这样就能保证 FIFO。

10、ZooKeeper 集群配置:

(1)卸载 openjdk,安装 jdk 环境,配置环境变量

(2)时间同步、防火墙处理

(3)配置主机名到 ip 地址的映射

[root@znode1 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.100.31 znode1
192.168.100.32 znode2
192.168.100.33 znode3

(4)三个节点分别:下载、解压 zookeeper-3.4.14.tar.gz 至安装目录(例:/usr/local/src/zookeeper-3.4.14)

(5)创建同名软链接

(6)修改 ZooKeeper 配置文件

[root@znode1 ~]# cd /usr/local/src/zookeeper/conf/
[root@znode1 conf]# cp zoo_sample.cfg zoo.cfg
[root@znode1 conf]# vim zoo.cfg
dataDir=/data/zookeeper
clientPort=2181
server.1=znode1:2888:3888    #心跳端口、选举端口
server.2=znode2:2888:3888
server.3=znode3:2888:3888

# 参数说明
tickTime=2000    #心跳间隔
initLimit=10     #Follower 服务器初始化连接时最长能忍受多少个心跳时间间隔数
syncLimit=5      # Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度
dataDir          #Zookeeper保存数据的目录,默认情况下Zookeeper将写数据的日志文件也保存在这个目录里。
clientPort       #这个端口就是客户端(应用程序)连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口接受客户端的访问请求。
server.A=B:C:D   #A 为数字,表示第几号服务器;B 是这个服务器的 IP 地址或主机名映射;C 第一个端口用来集群成员的信息交换,表示这个服务器与集群中的 Leader 服务器交换信息的端口;D 是在 leader 挂掉时专门用来进行选举 leader 所用的端口。

 (7)在数据目录下创建 myid 文件

[root@znode1 ~]# cat /data/zookeeper/myid 
1

(8)启动 ZooKeeper 集群

[root@znode1 ~]# /usr/local/src/zookeeper/bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/src/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@znode1 ~]# jps    # 查看进程
1397 Jps
1372 QuorumPeerMain
[root@znode1 ~]# /usr/local/src/zookeeper/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/src/zookeeper/bin/../conf/zoo.cfg
Mode: follower
[root@znode2 ~]# /usr/local/src/zookeeper/bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/src/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@znode2 ~]# jps
1291 Jps
1260 QuorumPeerMain
[root@znode2 ~]# /usr/local/src/zookeeper/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/src/zookeeper/bin/../conf/zoo.cfg
Mode: leader
[root@znode3 ~]# /usr/local/src/zookeeper/bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/src/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@znode3 ~]# jps
1330 QuorumPeerMain
1355 Jps
[root@znode3 ~]# /usr/local/src/zookeeper/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/src/zookeeper/bin/../conf/zoo.cfg
Mode: follower

(9)链接

[root@znode1 ~]# /usr/local/src/zookeeper/bin/zkCli.sh    # 本地链接
......
[zk: localhost:2181(CONNECTED) 0] 


[root@znode2 ~]# /usr/local/src/zookeeper/bin/zkCli.sh -server znode3    # 远程链接
......
[zk: znode3(CONNECTED) 0] 

(9)简单命令

  创建:create [-se] [path]

    -s:序列化;-e:临时节点

  ls、get、ls2

[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 1] create /data 123
Created /data
[zk: localhost:2181(CONNECTED) 2] ls /
[zookeeper, data]
[zk: localhost:2181(CONNECTED) 3] get /data
123
cZxid = 0x400000005
ctime = Fri May 22 18:23:24 CST 2020
mZxid = 0x400000005
mtime = Fri May 22 18:23:24 CST 2020
pZxid = 0x400000005
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: localhost:2181(CONNECTED) 4] ls2 /data
[]
cZxid = 0x400000005
ctime = Fri May 22 18:23:24 CST 2020
mZxid = 0x400000005
mtime = Fri May 22 18:23:24 CST 2020
pZxid = 0x400000005
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0

 

  删除:delete [-se] [path]

  rmr [path]

  quota

  setquota -n|-b [val] [path]

    n : 表示子节点的最大个数
    b : 表示数据值的最大长度
    val : 子节点最大个数或数据值的最大长度

  listquota

  delquota

  history

  redo

 

posted @ 2020-05-22 18:25  我听过  阅读(517)  评论(0编辑  收藏  举报