Zookeeper安装部署及原理-zab算法原理详解

1. zookeeper安装部署

1.1 系统环境及安装版本

系统环境:centos 7.8

安装版本:ZooKeeper CLI version: 3.6.2–803c7f1a12f85978cb049af5e4ef23bd8b688715, built on 09/04/2020 12:44 GMT

1.2 集群模式

单机模式:stand-clone

集群模式:多机多server

伪集群模式:单机多个server

1.3 安装部署单个节点

  • 下载软件包,配置jdk:
#wget https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.6.2/apache-zookeeper-3.6.2-bin.tar.gz
gunzi apache-zookeeper-3.6.2-bin.tar.gz
tar xf apache-zookeeper-3.6.2-bin.tar
cd apache-zookeeper-3.6.2-bin
cp zoo_sample.cfg zoo.cfg # 复制一组配置文件,zookeeper在启动之前默认是加载的zoo.cfg配置文件
# vim zoo.cfg
# The number of milliseconds of each tick
tickTime=2000 // 服务器心跳检测时间
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10 //投票选举leader的初始化时间
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5 // leader监听不到follower的心跳,从集群中将follower移除的时间
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/opt/zookeeper //数据目录
# the port at which the clients will connect
clientPort=2181  //客气端监听端口
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

## Metrics Providers
#
# https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true

# 配置jdk
下载jdk:https://www.oracle.com/cn/java/technologies/javase-downloads.html
解压等 略
配置JAVA_HOME
# cat /etc/profile
export JAVA_HOME=/usr/local/jdk-14.0.2
export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin
export CLASSPATH=.:$JAVA_HOME/lib
# JAVA_HOME: jdk软件包的路径
# PATH:是指命令搜索路径
# CLASSPATH:是指类搜索路径
[root@oracle ~]# java -version
openjdk version "14.0.2" 2020-07-14
OpenJDK Runtime Environment (build 14.0.2+12-46)
OpenJDK 64-Bit Server VM (build 14.0.2+12-46, mixed mode, sharing)
  • 启动zookeeper
[root@localhost apache-zookeeper-3.6.2-bin]# cd bin/
[root@localhost bin]# ls
README.txt    zkCli.cmd  zkEnv.cmd  zkServer.cmd            zkServer.sh            zkSnapShotToolkit.sh  zkTxnLogToolkit.sh
zkCleanup.sh  zkCli.sh   zkEnv.sh   zkServer-initialize.sh  zkSnapShotToolkit.cmd  zkTxnLogToolkit.cmd
[root@localhost bin]# ./zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /opt/apache-zookeeper-3.6.2-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
# 进入命令行
[root@localhost bin]# ./zkCli.sh 
Connecting to localhost:2181
2020-09-21 18:42:22,190 [myid:] - INFO  [main:Environment@98] - Client environment:zookeeper.version=3.6.2--803c7f1a12f85978cb049af5e4ef23bd8b688715, built on 09/04/2020 12:44 GMT
2020-09-21 18:42:22,191 [myid:] - INFO  [main:Environment@98] - Client environment:host.name=localhost
2020-09-21 18:42:22,192 [myid:] - INFO  [main:Environment@98] - Client environment:java.version=14.0.2
2020-09-21 18:42:22,192 [myid:] - INFO  [main:Environment@98] - Client environment:java.vendor=Oracle Corporation
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:java.home=/usr/local/jdk-14.0.2
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:java.class.path=/opt/apache-zookeeper-3.6.2-bin/bin/../zookeeper-server/target/classes:/opt/apache-zookeeper-3.6.2-bin/bin/../build/classes:/opt/apache-zookeeper-3.6.2-bin/bin/../zookeeper-server/target/lib/*.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../build/lib/*.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/zookeeper-prometheus-metrics-3.6.2.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/zookeeper-jute-3.6.2.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/zookeeper-3.6.2.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/snappy-java-1.1.7.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/slf4j-log4j12-1.7.25.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/slf4j-api-1.7.25.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/simpleclient_servlet-0.6.0.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/simpleclient_hotspot-0.6.0.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/simpleclient_common-0.6.0.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/simpleclient-0.6.0.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/netty-transport-native-unix-common-4.1.50.Final.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/netty-transport-native-epoll-4.1.50.Final.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/netty-transport-4.1.50.Final.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/netty-resolver-4.1.50.Final.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/netty-handler-4.1.50.Final.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/netty-common-4.1.50.Final.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/netty-codec-4.1.50.Final.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/netty-buffer-4.1.50.Final.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/metrics-core-3.2.5.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/log4j-1.2.17.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/json-simple-1.1.1.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jline-2.14.6.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jetty-util-9.4.24.v20191120.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jetty-servlet-9.4.24.v20191120.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jetty-server-9.4.24.v20191120.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jetty-security-9.4.24.v20191120.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jetty-io-9.4.24.v20191120.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jetty-http-9.4.24.v20191120.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/javax.servlet-api-3.1.0.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jackson-databind-2.10.3.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jackson-core-2.10.3.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/jackson-annotations-2.10.3.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/commons-lang-2.6.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/commons-cli-1.2.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../lib/audience-annotations-0.5.0.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../zookeeper-*.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../zookeeper-server/src/main/resources/lib/*.jar:/opt/apache-zookeeper-3.6.2-bin/bin/../conf:.:/usr/local/jdk-14.0.2/lib
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:java.library.path=/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:java.io.tmpdir=/tmp
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:java.compiler=<NA>
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:os.name=Linux
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:os.arch=amd64
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:os.version=3.10.0-1062.el7.x86_64
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:user.name=root
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:user.home=/root
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:user.dir=/opt/apache-zookeeper-3.6.2-bin/bin
2020-09-21 18:42:22,193 [myid:] - INFO  [main:Environment@98] - Client environment:os.memory.free=11MB
2020-09-21 18:42:22,194 [myid:] - INFO  [main:Environment@98] - Client environment:os.memory.max=247MB
2020-09-21 18:42:22,195 [myid:] - INFO  [main:Environment@98] - Client environment:os.memory.total=15MB
2020-09-21 18:42:22,204 [myid:] - INFO  [main:ZooKeeper@1006] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@215be6bb
2020-09-21 18:42:22,206 [myid:] - INFO  [main:X509Util@77] - Setting -D jdk.tls.rejectClientInitiatedRenegotiation=true to disable client-initiated TLS renegotiation
2020-09-21 18:42:22,211 [myid:] - INFO  [main:ClientCnxnSocket@239] - jute.maxbuffer value is 1048575 Bytes
2020-09-21 18:42:22,221 [myid:] - INFO  [main:ClientCnxn@1716] - zookeeper.request.timeout value is 0. feature enabled=false
Welcome to ZooKeeper!
JLine support is enabled
2020-09-21 18:42:22,305 [myid:localhost:2181] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1167] - Opening socket connection to server localhost/127.0.0.1:2181.
2020-09-21 18:42:22,308 [myid:localhost:2181] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1169] - SASL config status: Will not attempt to authenticate using SASL (unknown error)
2020-09-21 18:42:22,312 [myid:localhost:2181] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@999] - Socket connection established, initiating session, client: /127.0.0.1:49492, server: localhost/127.0.0.1:2181
2020-09-21 18:42:22,377 [myid:localhost:2181] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1433] - Session establishment complete on server localhost/127.0.0.1:2181, session id = 0x10000214bdb0000, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0] 

  • zoo.cfg参数配置解释
参数 描述
dataDir 存放内存数据库快照文件,同时用于集群的myid文件也存在这个文件里
dataLogDir 用于单独设置transaction log目录,transaction log分离可以避免和普通log还有快照竞争。
tickTime 心跳时间,为了确保client-server连接存在,以毫秒为单位。
clientPort 客户端监听端口
globalOoutstandingLimit client请求队列的最大长度,防止内存溢出,默认值为1000.
preAllocSize 预分配的transaction log空间block 为proAllocSizekb 默认block为64M.
snapCount 在snapCount个snapshot后写一次transaction log,默认值为100000。
traceFile 用于记录请求的log,打开会影响性能,用于debug,最好不要定义。
maxClientCnxns 最大并发客户端数,用于防止DOS攻击的,默认值为10.
clientPortBindAddress 指定client ip 端口
minSessionTimeout 最小的客户端session超时时间,毫秒
electionAlg 用于选举实现的参数,0以原始基于UDP的方式协作,1为不进行用户验证的基于UDP的快速选举2为进行用户验证基于UDP的快速选举;3为基于TCP的快速选举,默认为3
initLimit 多少个ticktime内,允许其他server连接并初始化数据,如果zookeeper管理的数据较大,则应该增大这个值。
syncLimit leader监听不到follower的心跳,从集群中将follower移除的时间
leaderServers leader是否接收客户端,默认值为yes ,leader负责协调更新,当更新吞量时,可以设置为不接受客户端连接,以便leader可以专注于同步协调工作。
server.x=[hostname]:n[:nn] 配置集群的主机信息。
group.x=nn[:nn] 分组信息,表示哪个组有哪些节点。
weight.x=n 权重信息。

1.4 部署集群

  • 环境描述
hostname ip address leader选举端口 服务器通信端口 客户端访问端口
master 172.16.70.207 2888 3888 2181
slave1 172.16.70.213 2888 3888 2181
slave2 172.16.70.205 2888 3888 2181
  • 下载软件,放到/usr/local目录下
scp -rp zookeeper/ 172.16.70.213:/usr/local/
scp -rp zookeeper/ 172.16.70.205:/usr/local/
scp -rp conf/zoo.cfg 172.16.70.213:/usr/local/zookeeper/conf/
scp -rp conf/zoo.cfg 172.16.70.205:/usr/local/zookeeper/conf/
scp -rp jdk-14.0.2 172.16.70.213:/usr/local/
scp -rp jdk-14.0.2 172.16.70.205:/usr/local/
[root@master local]# cat zookeeper/conf/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
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/opt/zookeeper
dataLogDir=/opt/zookeeper_log
# the port at which the clients will connect
clientPort=2181
...
server.0=172.16.70.207:2888:3888
server.1=172.16.70.213:2888:3888
server.2=172.16.70.205:2888:3888

server.A=B:C:D
A: 是一个数字,表示服务器的编号;
B:是服务器的ip地址
C:是leader选举的端口
D:zookeeper服务器之前的通信端口

  • 配置myid文件
# 在172.16.70.207上数据目录下配置
# 根据以下配置
server.0=172.16.70.207:2888:3888
server.1=172.16.70.213:2888:3888
server.2=172.16.70.205:2888:3888

[root@master local]# cat /opt/zookeeper/myid 
0

  • 配置环境变量
# 三个节点相同
cat >>/etc/profile<EOF
export JAVA_HOME=/usr/local/jdk-14.0.2
export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin
export CLASSPATH=.:$JAVA_HOME/lib
export PATH=/usr/local/zookeeper/bin:$PATH
EOF
source /etc/profile
  • 启动
# master
[root@master local]# zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@master local]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: leader

# slave1
[root@slave1 conf]# zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@slave1 conf]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower
# slave2
[root@slave2 zookeeper]# zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@slave2 zookeeper]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower

  • 报错总结

slave节点出现[root@slave1 zookeeper]# zkServer.sh status

ZooKeeper JMX enabled by default

Using config: /usr/local/zookeeper/bin/…/conf/zoo.cfg

Client port found: 2181. Client address: localhost. Client SSL: false.

Error contacting service. It is probably not running.

现象:服务已经启动,端口已经开启,在显示状态信息的时候显示报错

解决:是因为服务启动节点在寻找master,但是节点的防火墙是开启状态

iptables -F
iptables -X
iptables -Z
iptables -L

1.5 zookeeper命令详解

# zkCli.sh

连接命令:

connect 127.0.0.1:2181

关闭: close

节点命令:

创建节点:create /t1  v1
       -s 为顺序节点,-e为临时节点
查看节点:ls /t1 #用绝对路径
查看节点状态:stat /t1
     输出信息如下:
      cZxid = 0x20000000f
ctime = Tue Sep 22 19:56:22 CST 2020
mZxid = 0x20000000f
mtime = Tue Sep 22 19:56:22 CST 2020
pZxid = 0x20000000f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 1
numChildren = 0
输出信息解释:
cZxid:节点创建时的zxid
ctime:节点创建时间
mZxid:节点最近一次更新时的zxid
mtime:节点最近一次更新的时间
cversion:子节点数据更新次数
dataVersion:本节点数据更新次数
aclVersion:节点ACL(授权信息)的更新次数
ephemeralOwner:如果该节点为临时节点,ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是临时节点,ephemeralOwner值为0
dataLength:节点数据长度,本例中为hello world的长度
numChildren:子节点个数

查看节点的值:get /t1
删除节点: delete /a

配额命令:

显示配额:listquota /storm #前提是/storm已经存在
设置配额:setquota -n 2 /t1 设置/t1子节点的数量最大为2
setquota -b 100 /t1 设置/t1节点长度最大为100
删除配额:delquota 
   -n :子节点个数 -b 子节点数据长度
历史命令:history

认证命令:

用于认证: addauth digest username:password
设置节点的ACL:setAcl schema:id:permissions
获取节点的acl:getAcl 
scheme和id
world: 它下面只有一个id, 叫anyone, world:anyone代表任何人,zookeeper中对所有人有权限的结点就是属于world:anyone的
auth: 它不需要id, 只要是通过authentication的user都有权限(zookeeper支持通过kerberos来进行authencation, 也支持username/password形式的authentication)
digest: 它对应的id为username:BASE64(SHA1(password)),它需要先通过username:password形式的authentication
ip: 它对应的id为客户机的IP地址,设置的时候可以设置一个ip段,比如ip:192.168.1.0/16, 表示匹配前16个bit的IP段
super: 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)
permissions
CREATE(c): 创建权限,可以在在当前node下创建child node
DELETE(d): 删除权限,可以删除当前的node
READ(r): 读权限,可以获取当前node的数据,可以list当前node所有的child nodes
WRITE(w): 写权限,可以向当前node写数据
ADMIN(a): 管理权限,可以设置当前node的permission

强制同步命令:sync
由于请求在半数以上的zk server上生效就表示此请求生效,那么就会有一些zk server上的数据是旧的。sync命令就是强制同步所有的更新操作

设置和显示监视状态:printwatchers
退出:quit

2. zookeeper基本概念

2.1 zookeeper基础介绍

ZooKeeper 是Hadoop下的一个子项目,它是一个针对大型分布式系统的可靠协调系统;它提供的功能包括:配置维护、名字服务、分布式同步、组服务等; 它的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。

zookeeper的应用场景:用于担任服务生产者和服务消费者的注册中心。

2.2 zookeeper设计目标

zookeeper允许分布式进行通过共享的层次结构命名空间进行相互协调。zookeeper 的数据保存在内存中,意味着可以实现高吞吐量和低延迟。

名称空间由zookeeper中的数据寄存器组成,称为znode,这些类似于文件和目录。zookeeper层次结构是树型结构。

zookeeper层次结构命名空间示意图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rz3iVfJ8-1600947369116)(https://tcs-ga.teambition.net/storage/111xf8b9d95229eea5074d0d5d81b0b75bb0?Signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBcHBJRCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9hcHBJZCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9vcmdhbml6YXRpb25JZCI6IiIsImV4cCI6MTYwMTU1MTkwMywiaWF0IjoxNjAwOTQ3MTAzLCJyZXNvdXJjZSI6Ii9zdG9yYWdlLzExMXhmOGI5ZDk1MjI5ZWVhNTA3NGQwZDVkODFiMGI3NWJiMCJ9.MJNm85gXs9gPXOWydEGIAWAyYV5t11u8Qp9x2Qs8gcc&download=%E5%9B%BE%E7%89%87.png "")]

2.3 zookeeper的主要特点

1)、最终一致性:为客户端展示同一视图,这是 ZooKeeper 最重要的性能。

2)、可靠性:如果消息被一台服务器接受,那么它将被所有的服务器接受。

3)、实时性:ZooKeeper 不能保证两个客户端同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。

4)、等待无关(wait-free):慢的或者失效的 client 不干预快速的client的请求。

5)、原子性:更新只能成功或者失败,没有中间其它状态。

6)、顺序性:对于所有Server,同一消息发布顺序一致。

3. zookeeper原理说明

3.1 zookeeper 系统架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kwfqt35k-1600947369118)(https://tcs-ga.teambition.net/storage/111x8d56511b8515a6b48d81901c9c7067f1?Signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBcHBJRCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9hcHBJZCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9vcmdhbml6YXRpb25JZCI6IiIsImV4cCI6MTYwMTU1MTkwMywiaWF0IjoxNjAwOTQ3MTAzLCJyZXNvdXJjZSI6Ii9zdG9yYWdlLzExMXg4ZDU2NTExYjg1MTVhNmI0OGQ4MTkwMWM5YzcwNjdmMSJ9.2641HE6InZewXCIweWqEQF1XDMykEnEy6ZHt1uPbN0Q&download=%E5%9B%BE%E7%89%87.png "")]

zookeeper原理:

a. 如果客户端在服务器端执行读操作,首先,客户端向服务器端建立连接,发送tcp连接请求,server端接响应,获取观察的事件以及发送心跳,如果这个连接中断,client会向其他server发送连接请求,建立连接;

b. 如果是读请求,则客户端只需要连接到follower节点即可就能返回数据,如果是读请求client将会连接到leader端获取数据。上图每一个server都代表一个follower节点,每个follower节点组成一组zookeeper服务的集群。

c. 当zookeeper启动时,将从实例中选举一个leader,leader负责处理数据更新等操作,一个更新操作成功的标志是当且仅当大多数server在内存中成功修改数据。每个server在内存中存储了一份数据。

d. zookeeper集群是可以复制的,为了保证数据间的一致性,zookeeper是通过zab协议来保证的。

e.** zab协议内容:崩溃恢复,消息广播**

    1. 当集群中选举出一个leader之后,剩下的其他节点被称为follower,客气端发送的所有写请求都将由leader处理,leader接收之后,以广播的方式发送给follower.

    2.  当leader崩溃或失去大多数时,集群中重新开始选举leader,选举出来以后,让所有的服务器都恢复到一个正确的状态。

    3. 当选举出一个新的leader之后,会和其他follower同步数据。

4. zab协议

4.1 zab协议概念及作用

概念:zab协议的全称是zookeeper Atomic Broadcast(zookeeper原子广播),zookeeper是通过zab协议来保证分布式事务的一致和性。

zab协议是一种支持崩溃恢复和原子广播协议,借鉴paxos算法,专门为zookeeper设计的。

zab协议的作用:客户端连接到zookeeper集群中的一个节点,如果是读请求,则直接返回结果,如果是写请求,节点就会向leader提交事务之后leader向所有节点发送广播,如果超过半数执行成功,那么leader就会向client发送commit ok的消息。

zab协议的特性:

1)zab协议需要确保那些已经在leader服务器上提交的事务最终最所有的服务器提交;

    2)zab协议需要确保丢弃那些只在leader上被提出而没有被提交的事务。

4.2 zab协议的原理

zab协议的三个阶段: 发现,同步,广播

发现:选leader,zookeeper集群必须选举一个leader进程,同时leader会维护一个follower的可用客户端列表,客户端可以根据这个列表和follower进行通信。

同步:leader将自己本身的数据和follower进行同步,做成多副本,也提现了CAP中的高可用和分区容错性。

广播:leader接收客户端的写请求,被当做一个提案proposal,且赋予一个提案编号,leader根据提案编号将proposal发送给其他follower节点。

4.3 zab协议内容

zab协议包括:崩溃恢复和消息传播

崩溃恢复又包括:leader选举和数据恢复。

  • 状态转换:当集群中启动中,突然出现网络中断或者其他原因造成leader no online,zab协议就会进入崩溃恢复模式;之后选举出新的leader之后,向其他follower节点同步数据,数据一致后进入消息传播模式。

在进入消息传播模式中,client向leader发送写请求,会将每一个写请求做为一个事务请求转换为一个提案proposal,并且在广播事务proposal之前leader分配一个全局事务递增的唯一id,leader会根据事务的唯一id进行处理。

  • 消息传播具体步骤

a. 客户端发起一个写请求;

b. leader端将client发起的事务请求分配一个事务id,zxid;

c. leader服务器给每一个follower服务器分配一个队列,将新的proposal放到队列中,然后根据FIFO策略进行消息发送;

d. 当follower接收到leader的提案之后,首先将其以事务的方式写入到二进制中,写入成功之后返回给leader ack的消息;

e. 当leader接收到半数以上的ack消息之后,则认为消息发送成功,返回给client commit ok的消息;

f. 返回给client之后,leader也会将commit ok的消息返回给所有follower节点。

使用FIFO的优点:

leader服务器与每一个follower服务器之间都维护了一个FIFO消息队列来发送消息,可以做到异步解耦。如果使用同步的方式,就造成性能下降。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4yIFM41W-1600947369120)(https://tcs-ga.teambition.net/storage/111x76e6f92e1c63d7a4e52eafe1dad963e4?Signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBcHBJRCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9hcHBJZCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9vcmdhbml6YXRpb25JZCI6IiIsImV4cCI6MTYwMTU1MTkwMywiaWF0IjoxNjAwOTQ3MTAzLCJyZXNvdXJjZSI6Ii9zdG9yYWdlLzExMXg3NmU2ZjkyZTFjNjNkN2E0ZTUyZWFmZTFkYWQ5NjNlNCJ9.PgcKroRZ6nykIlnQgzbUTaM3d9L8W370TH9e7D0bRB8&download=%E5%9B%BE%E7%89%87.png "")]

4.4 zab协议保证数据一致性的因素

如果一个事务在leader上提交了,并且过半的节点都响应了ack,但是在leader发送commit消息之前挂机。

如果发生以上情况,那么zab协议为了保证数据一致性的要求:

  1. 确保已经被leader提交的proposal必须最终被所有的follower服务器提交;

2)确保丢弃已经被leader提出的但是没有被提交的proposal.

根据以上要求,zab协议则必须满足以下条件:

a. 新选举出来的leader不能包含未提交的proposal,即内存中的数据是干净的;

b. 新选举的leader节点中含有最大的zxid.

4.5 在zab数据同步过程中,怎么丢弃一个proposal

zxid: 是事务编号id,是一个64位的数字。

每当客户端连接一次写请求,leader都会分配一个zxid。其中低32位可以看成简单的递增的计数器,每请求一个次计数器加1,而高32位代表leader周期的epoch编号。

每当选举一次leader ,就会从这个leader服务器取出本地最大的事务号+1赋予epoch。以这样的方式来记录每个leader的生命周期。并将低32位的值,重置为0,重新生成txid;

当一台follower机器包含上一个leader周期中未提交的proposal的服务器启动时,这台机器加入集群和现在的leader关联之后,为了保证数据一致性,会和follower机器做比对,比对的结果是follower机器的proposal值大于leader的proposal,这时会将follower节点做回退,回退到一个确实已经被集群中过半的机器commit的最新的proposal.

4.6 zab和paxos

zookeeper的主要功能是维护一个高可用且一致的数据库,数据库内容复制在多个节点上,只要满足集群大多数系统就可用。实现这一核心的是ZAB算法,一种atomic Broadcast协议,形象的说就是能够保证发给各复本的消息顺序相同。

  • 那么zookeeper为什么不基于paxos协议呢?

首先paxos协议的一致性不能达到zookeeper的要求。例如

假设使用paxos协议,一开始集群中只有leader p1,他发起了两个事务,分别是t1 v1,t2 v2(序号tx的值为vx),假设这两个事务在写入的过程中挂了,又经过重新选举主为leader p2,他又发起了一个事务t1,v1,此时在p2里事务的顺序由新到旧是这样的(t1-v1,t2-v2,t1-v1)后者的t1-v1是leader1的,所以在执行事务时会将p1的t1-v1替换。即p2的t1-v1在前,p1的t2-v2在后。

那么我们都知道zookeeper集群架构是一个树形结构,假如我们在p1的t1-v1中创建/a目录 ,在t2-v2中创建/a/app_1文件,在p2的t1-v1中创建/b目录,由上可知p1的t1-v1已经被p2的t1-v1覆盖。由此变成/b,/a/app_1,那么a目录没有创建,执行/a/app_1也是不成功的。

  • 解决方案

为了保证这一点,zab要保证同一个leader的发起事务要按顺序被apply,同时还要保证只有先前的leader的所有事务都被apply之后,新选的leader才能发起事务。

zab的核心思想,形象的说就是保证任意时刻只有一个leader,所有更新事务由leader发起去更新所有复本,只要多数commit成功,就发给客户端commit ok的信息。因为zab处理的事务永远不会回滚,zab的2PC做了优化,多个事务只要通知zxid最大的那么commit,之前的各follower会统统commit.

4.7 zab的四个阶段

zab节点的三个状态:

following:当前节点是跟随者,服从leader节点的命令;

leading: 当前节点是leader,负责协调事务;

election/looking:节点处理选举状态,正在寻找leader ;

observer: 不参数选举,是只读节点;

节点的持久状态:

history:当前节点接收到事务 Proposal 的LogacceptedEpoch:Follower 已经接受的 Leader 更改 epoch 的 newEpoch 提议。currentEpoch:当前所处的 Leader 年代lastZxid:history 中最近接收到的Proposal 的 zxid(最大zxid)

  • 选举阶段

节点一开始处于选举节点,只要有一个节点选举得的票数超过半数,就可以当leader。

zookeeper规定所有的有效的票数必须在一个轮次里,每个服务器在开始新一轮投票时,都会对自己维护的logicalClock进行自增操作。

投票之前都会先将自己的logicalClock reset .投票格式为(1,2) (2,1)(3,1) 前者为投票者,后者为被选举者。

快速选举:

在选举阶段使用的是fast Leader Election(FLE)。

FLE 会选举拥有最新Proposal history (lastZxid最大)的节点作为 Leader,这样就省去了发现最新提议的步骤。这是基于拥有最新提议的节点也拥有最新的提交记录

成为 Leader 的条件:

1)选 epoch 最大的

2)若 epoch 相等,选 zxid 最大的

3)若 epoch 和 zxid 相等,选择 server_id 最大的(zoo.cfg中的myid)

节点在选举开始时,都默认投票给自己,当接收其他节点的选票时,会根据上面的 Leader条件 判断并且更改自己的选票,然后重新发送选票给其他节点。当有一个节点的得票超过半数,该节点会设置自己的状态为 Leading ,其他节点会设置自己的状态为 Following

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0uwsrUpK-1600947369121)(https://tcs-ga.teambition.net/storage/111x3cd1910450d5dd2878e851723e1b41be?Signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBcHBJRCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9hcHBJZCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9vcmdhbml6YXRpb25JZCI6IiIsImV4cCI6MTYwMTU1MTkwMywiaWF0IjoxNjAwOTQ3MTAzLCJyZXNvdXJjZSI6Ii9zdG9yYWdlLzExMXgzY2QxOTEwNDUwZDVkZDI4NzhlODUxNzIzZTFiNDFiZSJ9.b3_-U6I-djdQ3O8LFWm80nFS9-0kmdnEG2s3crBieBk&download=%E5%9B%BE%E7%89%87.png "")]

  • 发现阶段(descovery)

在这个阶段,followers和上一轮选举出的准leader进行通信,同步followers最近接收的事务proposal.

一个follower只会连接一个leader,如果一个follower节点认为另一个follower节点,则会在尝试连接时被拒绝,被拒绝之后,该节点就会进入leader election阶段。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1xyh1dfK-1600947369122)(https://tcs-ga.teambition.net/storage/111x749d7250011b3062eb115d0d76fb2865?Signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBcHBJRCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9hcHBJZCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9vcmdhbml6YXRpb25JZCI6IiIsImV4cCI6MTYwMTU1MTkwMywiaWF0IjoxNjAwOTQ3MTAzLCJyZXNvdXJjZSI6Ii9zdG9yYWdlLzExMXg3NDlkNzI1MDAxMWIzMDYyZWIxMTVkMGQ3NmZiMjg2NSJ9.FZdtFsRmPckl_XaWPtqIk72bbG0_u4_IJGoLptpAxHE&download=%E5%9B%BE%E7%89%87.png "")]

这个阶段的主要目的是发现当前大多数节点接收的最新 Proposal,并且准 Leader 生成新的 epoch ,让 Followers 接收,更新它们的 acceptedEpoch。

选举流程:

a. 候选节点A初始化自身的zxid和epoch;

b. 向其他所有节点发送选主通知;

c. 等待其他节点的回复;

d. 如果来自B节点的回复不为空,且B是一个有效的节点,判断B此时的运行状态是LOOKING(也在选 主)还是LEADING/FOLLOWING

  • 同步阶段

同步阶段主要是利用 Leader 前一阶段获得的最新 Proposal 历史,同步集群中所有的副本

只有当 quorum(超过半数的节点) 都同步完成,准 Leader 才会成为真正的 Leader。Follower 只会接收 zxid 比自己 lastZxid 大的 Proposal。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ff8sx3W1-1600947369123)(https://tcs-ga.teambition.net/storage/111x142175c11f004c5f2546727ffbbf2e01?Signature=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBcHBJRCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9hcHBJZCI6IjU5Mzc3MGZmODM5NjMyMDAyZTAzNThmMSIsIl9vcmdhbml6YXRpb25JZCI6IiIsImV4cCI6MTYwMTU1MTkwMywiaWF0IjoxNjAwOTQ3MTAzLCJyZXNvdXJjZSI6Ii9zdG9yYWdlLzExMXgxNDIxNzVjMTFmMDA0YzVmMjU0NjcyN2ZmYmJmMmUwMSJ9.8KRCtSbHyNDpt8jde-lv4uIMzr5ybNdZhNLXeMjr3gs&download=%E5%9B%BE%E7%89%87.png "")]

  • 广播阶段(broadcast)

zookeeper集群对外提交事务服务,并且leader可以进行消息广播。同时如果有新的节点加入,需要对新节点同步。

猜你喜欢

转载自blog.csdn.net/jiaona_chen123/article/details/108781636
今日推荐