1 概述

1.1 简介

ZooKeeper 是 Apache 的一个顶级项目,为分布式应用提供高效、高可用的分布式协调服务,提供了诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知和分布式锁等分布式基础服务。由于 ZooKeeper 便捷的使用方式、卓越的性能(基于内存)和良好的稳定性,被广泛地应用于诸如 Hadoop、HBase、Kafka 和 Dubbo 等大型分布式系统中。

官方地址:https://zookeeper.apache.org

1.2 角色

领导者(leader):负责进行投票的发起和决议,更新系统状态。

跟随者(follower):用于接收客户端请求并给客户端返回结果,在选主过程中进行投票。

观察者(observer):可以接受客户端连接,将写请求转发给 leader,但是observer 不参加投票的过程,只是为了扩展系统,提高读取的速度。

1.3 节点特性

ZooKeeper 节点的生命周期取决于节点的类型。在 ZooKeeper 中,节点根据持续时间可以分为持久节点(PERSISTENT)临时节点(EPHEMERAL),根据是否有序可以分为顺序节点(SEQUENTIAL)、和无序节点(默认是无序的)。每个客户端连接zookeeper会产生一个session,客户端连接关闭时session也会消失

持久节点一旦被创建,除非主动移除,不然一直会保存在 Zookeeper 中(不会因为创建该节点的客户端的会话失效而消失)。

1.3 数据模型

层次化的目录结构,命名符合常规文件系统规范,类似于文件目录。

每个节点在 Zookeeper 中叫做 Znode,并且其有一个唯一的路径标识。

每个节点中数据存储不能超过1M(不要把Zookeeper当数据库用)。

1.4 特性

顺序一致性。客户端的更新将按发送顺序应用。

原子性。要么成功要失败。没有中间状态(最终一致性)。

单个系统映像。由于zookeeper使用复制集群,无论客户端连接哪个节点都能看到相同的数据。

可靠性。即所有节点支持持久化。

及时性。存储在节点中的数据会在较短时间内及时同步。

2 zookeeper集群搭建

2.1 网络拓扑

2.2 环境准备

CentOS7 * 4

JDK8

Zookeeper3.6.3

2.3 安装

在q101服务器中配置好后发送到其他服务器。q101操作如下:

  1. wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.6.3/apache-zookeeper-3.6.3-bin.tar.gz --no-check-certificate
  2. tar xf apache-zookeeper-3.6.3-bin.tar.gz
  3. mv apache-zookeeper-3.6.3-bin /usr/local/zookeepe
  4. cd /usr/local/zookeeper/conf
  5. mv zoo_sample.cfg zoo.cfg
  6. vi zoo.cfg
  7. # 1. 修改dataDir路径为“/var/zookeeper”
  8. # 2. 增加集群节点信息,信息如下
  9. server.4=192.168.88.101:2888:3888
  10. server.3=192.168.88.102:2888:3888
  11. server.2=192.168.88.103:2888:3888
  12. server.1=192.168.88.104:2888:3888
  13. # 3. 保存退出
  14. # 创建配置文件中设置的持久化目录
  15. mkdir /var/zookeeper
  16. # 将节点信息中的节点ID写如myid中
  17. echo 4 > /var/zookeeper/myid
  18. # 修改环境变量 增加zookeeper信息
  19. # export ZOOKEEPER_HOME=/usr/local/zookeeper
  20. # 在export PATH末尾追加 :$ZOOKEEPER_HOME/bin
  21. vi /etc/profile
  22. source /etc/profile

详细配置说明

  1. # leader和follower心跳时间,用于维护节点是否存活。单位毫秒
  2. tickTime=2000
  3. # 初始延迟。当follower追随leader时,leader允许初始延迟时间。
  4. # 用tickTime乘以initLimit,此处为2000*10即20秒
  5. initLimit=10
  6. # 数据同步时间,及超过时间数据同步失败。
  7. # 用tickTime乘以syncLimit,此处为2000*5即10秒
  8. syncLimit=5
  9. # 持久化目录,不建议存在tmp下
  10. dataDir=/var/zookeeper
  11. # 客户端连接zookeeper端口号
  12. clientPort=2181
  13. # 允许客户端连接最大连接数
  14. #maxClientCnxns=60
  15. # dataDir中保留快照数量
  16. #autopurge.snapRetainCount=3
  17. # 清除任务时间间隔,单位小时。0表示禁用自动清楚功能
  18. #autopurge.purgeInterval=1
  19. # 自定义监控
  20. #metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
  21. #metricsProvider.httpPort=7000
  22. #metricsProvider.exportJvmInfo=true
  23. # 此处为自己添加的配置。集群信息,投票过半数量=(集群节点行数 除以 2)+1
  24. # 数字1,2,3,4为节点ID,用于谦让出leader,投票过半后数字最大的为leader
  25. # 端口说明:当leader挂掉后(或第一次启动没有leader时候)其他节点通过3888建立连接进行投票选出leader,
  26. # 选中leader后,leader会通过2888端口来与follower进行工作
  27. server.4=192.168.88.101:2888:3888
  28. server.3=192.168.88.102:2888:3888
  29. server.2=192.168.88.103:2888:3888
  30. server.1=192.168.88.104:2888:3888

将q101相关文件传到q102并修改myid

  1. scp -r /usr/local/zookeeper 192.168.88.102:/usr/local/
  2. scp -r /var/zookeeper 192.168.88.102:/var/
  3. scp /etc/profile 192.168.88.102:/etc
  4. # 切换到q102服务器执行,数字与配置文件中的节点ID数据一致
  5. echo 3 > /var/zookeeper/myid
  6. # q102执行 使环境变量生效
  7. source /etc/profile

将q101相关文件传到q103并修改myid

  1. scp -r /usr/local/zookeeper 192.168.88.103:/usr/local/
  2. scp -r /var/zookeeper 192.168.88.102:/var/
  3. scp /etc/profile 192.168.88.103:/etc
  4. # 切换到q103服务器执行
  5. echo 2 > /var/zookeeper/myid
  6. source /etc/profile

将q101相关文件传到q104并修改myid

  1. scp -r /usr/local/zookeeper 192.168.88.103:/usr/local/
  2. scp -r /var/zookeeper 192.168.88.102:/var/
  3. scp /etc/profile 192.168.88.104:/etc
  4. # 切换到q104服务器执行
  5. echo 1 > /var/zookeeper/myid
  6. source /etc/profile

2.4 启动

为了测试我们采用前台启动,4台节点分别执行

  1. zkServer.sh start-foreground

启动前3台的时候会发现报错,连接其他节点失败,属于正常情况,当启动最后一台的时候发现没有发错了。原因是启动前2台的时候没有leader。当第3台启动完成的时候,此时节点数量已经超过半数,所以开始根据zoo.cfg配置文件中的服务ID进行选举,因为q101服务器ID 设置的4,按节点谦让规则q101为leader。

启动错误信息如下:



节点角色验证

在q101执行

  1. zkServer.sh status

3 zookeeper常用功能

3.1 常用命令

启动关闭

后台启动:zkServer.sh start

前台启动:zkServer.sh start-foreground

停止:zkServer.sh stop

重启:zkServer.sh restart

查看版本:zkServer.sh version

查看状态:zkServer.sh status

连接与退出

连接zookeeper客户端:zkCli.sh -server 127.0.0.1:2181 连接本机可以直接输入zkCli.sh

退出zookeeper客户端:quit

操作命令

查看zookeeper所有命令:连接到zookeeper客户端后输入help

查看根目录下包含的节点:ls /

查看节点状态信息:ls2 / 或者使用 ls -s /

创建一个非顺序的持久化节点:create [-s] [-e] path data acl 比如 create /test test-1

创建一个临时节点:create -e /test/tmp tem-data

创建一个顺序节点:create -s /test/aaa aaa-data

删除一个节点:delete /test

  1. [zk: localhost:2181(CONNECTED) 0] ls /
  2. [zookeeper]
  3. [zk: localhost:2181(CONNECTED) 1] create /abc
  4. Created /abc
  5. [zk: localhost:2181(CONNECTED) 3] ls /
  6. [abc, zookeeper]
  7. # 创建节点后默认数据为null
  8. [zk: localhost:2181(CONNECTED) 9] get /abc
  9. null
  10. [zk: localhost:2181(CONNECTED) 4] create /abc/abcd
  11. Created /abc/abcd
  12. [zk: localhost:2181(CONNECTED) 15] set /abc/abcd hello
  13. [zk: localhost:2181(CONNECTED) 16] get /abc/abcd
  14. hello

3.2 stat结构

Zookeeper每个znode都有一个与之关联的stat结构,类似于Unix/Linux文件系统中文件的stat结构。通过stat /xxx/xxx查看

  1. [zk: localhost:2181(CONNECTED) 17] stat /abc/abcd
  2. cZxid = 0x100000003
  3. ctime = Wed Mar 02 23:41:34 CST 2022
  4. mZxid = 0x100000006
  5. mtime = Wed Mar 02 23:43:32 CST 2022
  6. pZxid = 0x100000003
  7. cversion = 0
  8. dataVersion = 2
  9. aclVersion = 0
  10. ephemeralOwner = 0x0
  11. dataLength = 5
  12. numChildren = 0

字段含义

cZxid:创建节点znode时的事务ID由leader维护的递增计数器,共64位,其中前32为表示leader纪元,即leader革新换代次数,比如上面的0x1表示目前leader为做变更,如当前leader关闭后重新选举的leader此处会变成0x2,并且后32位的事务ID重新开始;后32为表示所有的增删改操作次数。

mZxid:修改节点znode更改的事务ID。

pZxid:添加或删除子节点znode更改的事务ID。

ctime:表示从1970-01-01T00:00:00Z开始以毫秒为单位的znode创建时间。

mtime:表示从1970-01-01T00:00:00Z开始以毫秒为单位的znode最近修改时间。

dataVersion:表示对该znode的数据所做的更改次数。

cversion:表示对此znode的子节点进行的更改次数。

aclVersion:版本号,即znode的ACL进行更改的次数。

ephemeralOwner:如果znode是ephemeral类型节点,则这是znode所有者的 session ID。 如果znode不是ephemeral节点,则该字段设置为零。

dataLength:这是znode数据字段的长度。

numChildren:这表示znode的子节点的数量。

3.3 临时节点

临时节点伴随着session会话,当会话结束后临时节点销毁

  1. 通过q101连接zookeeper客户端查看日志

通过日志发现sessionID为:0x400000595cc0001

2. 创建临时节点 create -e /xxxx

  1. create -e /aaa
  1. 通过stat查看发现刚创建的临时节点“bbbb”的ephemeralOwner正是连接时候的sessionID



    当使用另一台服务器q102连接zkCli后查看aaa节点stat sessionID依旧是q101连接的sessionID。

3.4 持久序列

通过create -s path data可以创建持久序列,znode被创建后,znode名称会自动添加一个编号,编号会自动递增。编号的递增和节点名称无关.



编号的递增不会因为断开而重置,也不会因为zookeeper重启而重置

4 zookeeper原理

4.1 Paxos算法

它是一个基于消息传递的一致性算法,Leslie Lamport在1990年提出,近几年被广泛应用于分布式计算中,Google的Chubby,Apache的Zookeeper都是基于它的理论来实现的,Paxos还被认为是到目前为止唯一的分布式一致性算法,其它的算法都是Paxos的改进或简化。有个问题要提一下,Paxos有一个前提:Paxos只有在一个可信的计算环境中才能成立,这个环境是不会被入侵所破坏的。

关于Paxos的具体描述可以参考:https://baike.baidu.com/item/Paxos算法

4.2 ZAB协议

ZAB(ZooKeeper Atomic broadcast)即ZooKeeper原子消息广播协议,类似于一个二阶段提交过程(2PC)属于最终一致性。是zookeeper基于Paxos算法的简化实现。

所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为 Leader服务器,而余下的其他服务器则成为 Follower服务器。 Leader服务器负责将一个客户端事务请求转换成一个事务 Proposal(提议),并将该 Proposal分发給集群中所有的Follower服务器。之后 Leader服务器需要等待所有 Follower服务器的反馈,一旦超过半数的 Follower服务器进行了正确的反馈后,那么 Leader就会再次向所有的 Follower服务器分发 Commit消息,要求其将前一个 Proposal进行提交。

上图说明:

1:Client向Follower发出写操作;

2:Follower将收到的写请求转发给Leader处理;

3:Leader收到请求后分配一个全局单调递增的唯一的事务ID(即ZXID,按其先后顺序来进行排序与处理);

4.1:Leader服务器会为每一个 Follower服务器都各自分配一个单独的队列,然后将需要广播的事务Proposal依次放入这些队列中去,并且根据FIFO策略进行消息发送;每一个 Follower服务器在接收到这个事务 Proposal之后,都会首先将其以事务日志的形式写入到本地磁盘中去,并且在成功写入后反馈给 Leader服务器个Ack响应。Leader自己也会将事务日志写入磁盘;

4.2:当 Leader服务器接收到超过半数Follower的Ack响应后,就会广播一个Commit消息给所有的 Follower服务器以通知其进行事务提交(写入内存),同时 Leader自身也会完成对事务的提交。

5:由Leader将结果返回给Client1

上图中4.1和4.2步骤,由于Leader自身也计一票,如果Follower2 由于网络等原因没有给Leader Ack响应,但Leader和Follower 1 两票已超过半数,所以结果依然会成功。此时Client2请求Follower2获取“/aaa”数据时,可以通过sync 让Follower 2向Leader同步后再返回数据。

4.3 选举机制

4.3.1 节点网络

基于上面的第2章集群搭建,查看每台节点网络连接情况

  1. yum install net-tools
  2. netstat -natp | egrep '(2888|3888)'

查看结果如下:

  1. # q101信息
  2. [root@q101 ~]# netstat -natp | egrep '(2888|3888)'
  3. tcp6 0 0 192.168.88.101:3888 :::* LISTEN 1924/java
  4. tcp6 0 0 192.168.88.101:58052 192.168.88.102:3888 ESTABLISHED 1924/java
  5. tcp6 0 0 192.168.88.101:39738 192.168.88.103:3888 ESTABLISHED 1924/java
  6. tcp6 0 0 192.168.88.101:54174 192.168.88.102:2888 ESTABLISHED 1924/java
  7. tcp6 0 0 192.168.88.101:47726 192.168.88.104:3888 ESTABLISHED 1924/java
  8. # q102信息
  9. [root@q102 ~]# netstat -natp | egrep '(2888|3888)'
  10. tcp6 0 0 192.168.88.102:2888 :::* LISTEN 1661/java
  11. tcp6 0 0 192.168.88.102:3888 :::* LISTEN 1661/java
  12. tcp6 0 0 192.168.88.102:3888 192.168.88.101:58052 ESTABLISHED 1661/java
  13. tcp6 0 0 192.168.88.102:2888 192.168.88.103:38244 ESTABLISHED 1661/java
  14. tcp6 0 0 192.168.88.102:50104 192.168.88.104:3888 ESTABLISHED 1661/java
  15. tcp6 0 0 192.168.88.102:2888 192.168.88.104:48034 ESTABLISHED 1661/java
  16. tcp6 0 0 192.168.88.102:2888 192.168.88.101:54174 ESTABLISHED 1661/java
  17. tcp6 0 0 192.168.88.102:37466 192.168.88.103:3888 ESTABLISHED 1661/java
  18. # q103信息
  19. [root@q103 ~]# netstat -natp | egrep '(2888|3888)'
  20. tcp6 0 0 192.168.88.103:3888 :::* LISTEN 1415/java
  21. tcp6 0 0 192.168.88.103:38244 192.168.88.102:2888 ESTABLISHED 1415/java
  22. tcp6 0 0 192.168.88.103:3888 192.168.88.101:39738 ESTABLISHED 1415/java
  23. tcp6 0 0 192.168.88.103:3888 192.168.88.102:37466 ESTABLISHED 1415/java
  24. tcp6 0 0 192.168.88.103:58344 192.168.88.104:3888 ESTABLISHED 1415/java
  25. # q104信息
  26. [root@q104 ~]# netstat -natp | egrep '(2888|3888)'
  27. tcp6 0 0 192.168.88.104:3888 :::* LISTEN 1333/java
  28. tcp6 0 0 192.168.88.104:3888 192.168.88.101:47726 ESTABLISHED 1333/java
  29. tcp6 0 0 192.168.88.104:3888 192.168.88.102:50104 ESTABLISHED 1333/java
  30. tcp6 0 0 192.168.88.104:3888 192.168.88.103:58344 ESTABLISHED 1333/java
  31. tcp6 0 0 192.168.88.104:48034 192.168.88.102:2888 ESTABLISHED 1333/java

通过上面的网络信息整体网络连接图如下



上图说明:

zookeeper Leader和Follower连接主要通过2888和3888,2888主要用于leader与follower进行工作,3888用于Leader选举。每个zookeeper节点都会与其他节点建立连接。

4.3.2 选举流程

情况一:第一次启动

以上面我们搭好的4台节点为例,myid和zxid情况如下:

节点名称 myid zxid
q101 4 0
q102 3 0
q103 2 0
q104 1 0
选举前提当投票数量超过半数时才有效,此处4台节点,当第一次启动时候zxid都为0。只要有3台节点启动就可以选举出Leader。虽然说q101的myid=4但是如果最后启动q101,那么第一次启动时一定是q102成为Leader。

情况二:zxid不为0时,当集群重启,或Leader挂了的时候

当Leader挂了后,zookeeper集群会进行推选制选举,会优先选举数据最全的节点作为Leader(通过比较zxid,zxid越大代表该节点数据最全),如果zxid相同再比较myid。

节点名称 myid zxid
q101 4 0x100000009
q103 2 0x100000010
q104 1 0x100000010

原Leader q102挂掉后无论哪个节点先发现Leader挂掉,都会推选zxid最大的节点,q103和q104 zxid 都为xxx10再比较myid。所以此处q103最终会被选为新的Leader。

5 watch

  1. Watch是轻量级的,其实就是本地JVM的Callback,服务器端只是存了是否有设置了Watcher的布尔类型。
  2. 在服务端,在FinalRequestProcessor处理对应的Znode操作时,会根据客户端传递的watcher变量,添加到对应的ZKDatabase(org.apache.zookeeper.server.ZKDatabase)中进行持久化存储,同时将自己NIOServerCnxn做为一个Watcher callback,监听服务端事件变化
  3. Leader通过投票通过了某次Znode变化的请求后,然后通知对应的Follower,Follower根据自己内存中的zkDataBase信息,发送notification信息给zookeeper客户端。
  4. Zookeeper客户端接收到notification信息后,找到对应变化path的watcher列表,挨个进行触发回调。

Zookeeper集群搭建及原理的更多相关文章

  1. Zookeeper集群搭建以及python操作zk

    一.Zookeeper原理简介 ZooKeeper是一个开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等. Zookeeper设计目 ...

  2. Kafka学习之(五)搭建kafka集群之Zookeeper集群搭建

    Zookeeper是一种在分布式系统中被广泛用来作为:分布式状态管理.分布式协调管理.分布式配置管理.和分布式锁服务的集群.kafka增加和减少服务器都会在Zookeeper节点上触发相应的事件kaf ...

  3. ZooKeeper集群搭建过程

    ZooKeeper集群搭建过程 提纲 1.ZooKeeper简介 2.ZooKeeper的下载和安装 3.部署3个节点的ZK伪分布式集群 3.1.解压ZooKeeper安装包 3.2.为每个节点建立d ...

  4. Linux下zookeeper集群搭建

    Linux下zookeeper集群搭建 部署前准备 下载zookeeper的安装包 http://zookeeper.apache.org/releases.html 我下载的版本是zookeeper ...

  5. java 学习笔记(三)ZooKeeper集群搭建实例,以及集成dubbo时的配置 (转)

    ZooKeeper集群搭建实例,以及集成dubbo时的配置 zookeeper是什么: Zookeeper,一种分布式应用的协作服务,是Google的Chubby一个开源的实现,是Hadoop的分布式 ...

  6. 分布式架构中一致性解决方案——Zookeeper集群搭建

    当我们的项目在不知不觉中做大了之后,各种问题就出来了,真jb头疼,比如性能,业务系统的并行计算的一致性协调问题,比如分布式架构的事务问题, 我们需要多台机器共同commit事务,经典的案例当然是银行转 ...

  7. kafka学习(二)-zookeeper集群搭建

    zookeeper概念 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名 服务等.Zookeeper是h ...

  8. 分布式协调服务Zookeeper集群搭建

    分布式协调服务Zookeeper集群搭建 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.安装jdk环境 1>.操作环境 [root@node101.yinzhengjie ...

  9. Zookeeper 集群搭建--单机伪分布式集群

    一. zk集群,主从节点,心跳机制(选举模式) 二.Zookeeper集群搭建注意点 1.配置数据文件 myid 1/2/3 对应 server.1/2/3 2.通过./zkCli.sh -serve ...

随机推荐

  1. Floyd 循环检测算法(快慢指针法/龟兔指针法)

    Floyd Cycle Detection Algorithm   Floyd Cycle Detection Algorithm,即 Floyd 循环检测算法,又称快慢指针法.龟兔指针法.该算法用于 ...

  2. python15day

    昨日回顾 装饰器:完美的呈现了开放封闭原则.本质:闭包. def wrapper(f): def inner(*args,**kwargs): '''在执行被装饰函数之前,想写什么代码写什么代码''' ...

  3. python 小兵(5)参数

    我们目前为止,已经可以完成一些软件的基本功能了,那么我们来完成这样一个功能:约x 1 2 3 4 5 pint("拿出手机") print("打开陌陌") pr ...

  4. python 中 *args he **kwargs的区别

    ''' 一 *args 和 **kwargs 的区别? *args 表示任意个 无名参数, 类型是元祖 tuple. **kwargs 表示的是关键字的参数 传入的参数是 dict 类型. 当*和** ...

  5. ESP32:蓝牙BLE控制M3508电机

    ESP32:蓝牙BLE控制M3508电机 先给各位朋友拜个年,祝大家新春快乐,事事顺利,身体健康啊! 还是熟悉的3508,内容概述: ESP32主控 蓝牙BLE通信 使用实时系统(FreeRTOS) ...

  6. ARC-124 部分题解

    E 直接统计原式不好做,注意到首先我们应该知道怎样的 \(x\) 序列是合法的,那么不妨首先来统计一下合法的 \(x\) 序列数量. 令 \(b_i\) 为 \(i\) 向右给的球数,那么有(\(i ...

  7. Spring service本类中方法互相调用事物失效问题

    简介 Spring事物利用的是AOP,动态代理采用CGLIB代理(默认,也可以用Proxy代理,但是Proxy代理效率低于CGLIB代理).故只要弄懂Spring的AOP实现,就知道为什么servic ...

  8. SpringBoot使用IDEA设置的外部Tomcat启动

    前言 使用springboot内嵌的tomcat启动是没问题,但是工程是要放到服务器上的tomcat的,所以springboot内嵌的能够启动,但不代表服务器的tomcat能启动起来,我就遇到了这个问 ...

  9. 入门- k8s发布应用服务 (五)

    目标 了解 Kubernetes 的 Service(服务) 了解 Labels(标签)和 LabelSelector(标签选择器)与 Service(服务)的关系 在 kubernetes 集群中, ...

  10. NSLog 和printf区别

    NSLog是Foundation框架供的Objective-C日志输出函数,与标准C中的printf函数类似,并可以格式化输出. NSLog传递进去的格式化字符是NSString的对象,而不是char ...