0%

分布式协调服务-Zookeeper简研

简介

ZooKeeper是一种为分布式应用所设计的高可用、高性能且一致的开源协调服务,它提供了一项基本服务:分布式锁服务。由于ZooKeeper的开源特性,后来我们的开发者在分布式锁的基础上,摸索了出了其他的使用方法:配置维护、组服务、分布式消息队列、分布式通知/协调等。ZooKeeper性能上的特点决定了它能够用在大型的、分布式的系统当中。从可靠性方面来说,它并不会因为一个节点的错误而崩溃。除此之外,它严格的序列访问控制意味着复杂的控制原语可以应用在客户端上。ZooKeeper在一致性、可用性、容错性的保证,也是ZooKeeper的成功之处,它获得的一切成功都与它采用的协议——Zab协议是密不可分的。

分布式协调服务用以解决分布式应用中数据一致性问题,目前,在分布式协调技术方面做得比较好的就是Google的Chubby,但由于Chubby为Google自家使用,并未开源,于是就有了雅虎模仿出来的Zookeeper,并作为开源程序捐献给了Apache。了解更多可以参考官网:http://zookeeper.apache.org

只求快速理解,不求准确的描述下Zookeeper,可以把Zookeeper理解为一个高可用、能够实时保证数据一致性的内存数据库,其每一个节点最多可存储1M的数据量,实际使用应远远小于该值。

角色

Zookeeper是一个由多个Server(一般为奇数个)组成的集群,该集群有一个Leader,多个Follower。客户端可以连接任意ZooKeeper服务节点来读写数据
但在ZooKeeper的3.3.3版本以后,ZooKeeper中又添加了一种新角色Observer。Observer的作用同Follower类似,唯一区别就是它不参与选主过程。

观察上图的zookeeper的写请求过程,leader接到写请求后,会先发起一个提议,然后其他server对该提议提出投票,之后leader收集投票,只有当leader收取了超过半数的投票之后才会向其他服务器发起写通知,然后发起写请求的server返回结果给client。当集群中follower的数量增加后,由于leader需要收取更多server的投票,所以必然导致读效率变低。

Observer角色的引入在增加读server的同时,不增加投票server的数量,扩展集群后对写操作几乎没有影响。

Znode

ZooKeeper的数据模型,是类似于文件系统的一个层次命名空间。但必须使用绝对路径,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分。具有原子性,即写操作将替换掉整个znode中的数据,分为临时节点(不可拥有子节点)和永久节点

watch

Zookeeper客户端在数据节点上设置监视,则当数据节点发生变化时,客户端会收到提醒。ZooKeeper中的各种读请求,如getDate(),getChildren(),和exists(),都可以选择加”监视点”(watch)。”监视点”指的是一种一次性的触发器(trigger),当受监视的数据发生变化时,该触发器会通知客户端。

需注意:

  • “监视点”是一次性的,当触发过一次之后,除非重新设置,新的数据变化不会提醒客户端。
  • “监视点”将数据改变的通知客户端。如果数据改变是客户端A引起的,不能保证”监视点”通知事件会在引发数据修改的函数返回前到达客户端A
  • 对于”监视点”,ZooKeeper有如下保证:客户端一定是在接收到”监视”事件(watch event)之后才接收到数据的改变信息。

ZooKeeper读写机制

ZooKeeper集群中的任意server包括leader都可以接收客户端的请求;其中读请求可直接通过读取本地数据恢复给客户端;写请求会先发送给leader,再由leader发送其他server操作结果,然后该server返回结果给客户端。

ZooKeeper自身的高可用

我们知道,同一时间客户端仅能连接一台zookeeper机器中的server,这也就意味着,如果我们在客户端写死其中一台zookeeper server的IP的话,并不会达到高可用的目的。很幸运zookeeper的设计者已经想到了这个问题,所以每个ZooKeeper客户端的配置中都包括集合体中服务器的列表,在启动时,客户端会尝试连接到列表中的一台服务器。如果连接失败,它会尝试连接另一台服务器,以此类推,直到成功与一台服务器建立连接或因为所有ZooKeeper服务器都不可用而失败。
另外,zookeeper还拥有一系列的措施,保证少于半数的server故障后,客户端仍然不会丢失任何任务。

安装使用

下载软件包,请移步http://zookeeper.apache.org,这里使用zookeeper-3.5.2-alpha.tar.gz。
开始之前请确保已安装java,存在JAVA_HOME,(java1.6/1.7都可以)

1
2
3
4
5
6
cat /etc/profile.d/java.sh 
#!/bin/sh
JAVA_HOME=/usr/local/java
PATH=$JAVA_HOME/bin:$PATH

export JAVA_HOME PATH

我们这里用一台服务器模拟3个zookeeper服务:

1
2
3
4
5
tar -xzf zookeeper-3.5.2-alpha.tar.gz -C /usr/local/
cd /usr/local/
ln -sv zookeeper-3.5.2-alpha/ zk
cd /usr/local/zk/conf
mv zoo_sample.cfg zoo_sample.cfg.20170603 #将模板配置文件改名

添加环境变量

1
2
3
4
5
6
7
8
9
10
vim /etc/profile.d/zk.sh    #新建zk.sh,内容如下
cat /etc/profile.d/zk.sh
#!/bin/bash

export ZOOKEEPER_HOME=/usr/local/zk
#export PATH=.:$HADOOP_HOME/bin:$ZOOKEEPER_HOME/bin:$JAVA_HOME/bin:$PATH
export PATH=.:$HADOOP_HOME/bin:$ZOOKEEPER_HOME/bin:$PATH
#使文件生效
chmod +x /etc/profile.d/zk.sh
source /etc/profile.d/zk.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
cd /usr/local/zk/conf
vim zoo1.cfg #编辑启动配置文件,下面是我的配置文件
cat zoo1.cfg
tickTime=2000 #zookeeper服务端、服务端与客户端之间心跳间隔,单位是ms
dataDir=/var/lib/zookeeper/zk1 #数据文件位置,后面我们会用到
clientPort=2181 #客户端连接的端口
initLimit=5 #集群中follower连接leader的时间,以tickTime为单位,超时则该follower连接失败
syncLimit=2 #Leader与Follower之间发送消息时,请求和应答时间长度。
dataLogDir=/usr/local/zk/logs/zk1 #日志文件位置

server.1=localhost:2888:3888 #这里的server.X中的X为服务器编号,后面分别为IP或域名、Leader选举的端口、Zookeeper服务器之间的通信端口
server.2=localhost:2889:3889
server.3=localhost:2890:3890

同理,我们需要另外2个配置文件zoo2.cfg和zoo3.cfg,注意需要修改对应的dataDir、clientPort、dataLogDir;
启动服务之前,我们需要手动添加dataDir、dataLogDir目录;
另外需要注意dataDir目录需手动创建myid文件,文件内容为对应的server.X中的X,不手动创建或创建不正确会导致服务无法启动。

1
2
3
4
5
6
cd /var/lib/zookeeper/zk1   #cd到dataDir
echo 1 > myid #创建myid文件,内容为server.X中的X
cd /var/lib/zookeeper/zk2
echo 2 > myid
cd /var/lib/zookeeper/zk3
echo 3 > myid

启动服务

1
2
3
4
5
6
7
8
9
10
11
12
[root@test2 conf]# zkServer.sh start zoo1.cfg
ZooKeeper JMX enabled by default
Using config: /usr/local/zk/bin/../conf/zoo1.cfg
Starting zookeeper ... STARTED
[root@test2 conf]# zkServer.sh start zoo2.cfg
ZooKeeper JMX enabled by default
Using config: /usr/local/zk/bin/../conf/zoo2.cfg
Starting zookeeper ... STARTED
[root@test2 conf]# zkServer.sh start zoo3.cfg
ZooKeeper JMX enabled by default
Using config: /usr/local/zk/bin/../conf/zoo3.cfg
Starting zookeeper ... STARTED

用户在客户端可以通过telnet或nc向Zookeeper提交相应的命令,例如conf命令来查看配置信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@test2 conf]# echo conf | nc localhost 2181
clientPort=2181
secureClientPort=-1
dataDir=/usr/local/zk/logs/zk1/version-2
dataDirSize=67108880
dataLogDir=/var/lib/zookeeper/zk1/version-2
dataLogSize=1175
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=1
initLimit=5
syncLimit=2
electionAlg=3
electionPort=3888
quorumPort=2888
peerType=0
membership:
server.1=localhost:2888:3888:participant
server.2=localhost:2889:3889:participant
server.3=localhost:2890:3890:participant
version=100000000

ZooKeeper命令行客户端,help可以查看帮助,简单输入几个命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@test2 ~]# zkCli.sh -server 127.0.0.1:2181
Connecting to 127.0.0.1:2181
...省略部分输出...
WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: 127.0.0.1:2181(CONNECTED) 0] ls /
[zookeeper]
[zk: 127.0.0.1:2181(CONNECTED) 1] create /hello byFred
Created /hello
[zk: 127.0.0.1:2181(CONNECTED) 2] ls /
[hello, zookeeper]
[zk: 127.0.0.1:2181(CONNECTED) 3] get /hello
byFred

此时登录其他server端,也可以看到新增数据:

1
2
3
4
5
6
7
8
[root@test2 ~]# zkCli.sh -server 127.0.0.1:2183
...省略部分输出...
[zk: 127.0.0.1:2183(CONNECTED) 1] ls /
[hello, zookeeper]
[zk: 127.0.0.1:2183(CONNECTED) 2] delete /hello
[zk: 127.0.0.1:2183(CONNECTED) 3] ls /
[zookeeper]
[zk: 127.0.0.1:2183(CONNECTED) 4]

服务查看与关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#查看服务角色,可以看到zoo2为leader,其他两个为follower
[root@test2 conf]# zkServer.sh status zoo1.cfg
ZooKeeper JMX enabled by default
Using config: /usr/local/zk/bin/../conf/zoo1.cfg
Client port found: 2181\. Client address: localhost.
Mode: follower
[root@test2 conf]# zkServer.sh status zoo2.cfg
ZooKeeper JMX enabled by default
Using config: /usr/local/zk/bin/../conf/zoo2.cfg
Client port found: 2182\. Client address: localhost.
Mode: leader
[root@test2 conf]# zkServer.sh status zoo3.cfg
ZooKeeper JMX enabled by default
Using config: /usr/local/zk/bin/../conf/zoo3.cfg
Client port found: 2183\. Client address: localhost.
Mode: follower
# 停止服务
[root@test2 conf]# zkServer.sh stop zoo1.cfg

注意,启动服务之后zookeeper可能会自动修改配置文件内容,然后将你自己写的原文件备份为.bak文件

典型应用场景

ZooKeeper可以应用到数据发布与订阅、统一命名服务(Name Service)、分布通知/协调(Distribution of notification/coordination)、分布式锁(Distribute Lock)、集群管理(Cluster Management)、队列管理等等应用场景中,由于水平有限,有兴趣的同学请移步:邬兴亮的博客

参考:
http://www.cnblogs.com/wuxl360/category/874409.html
http://zookeeper.apache.org/doc/trunk/zookeeperStarted.html