ZooKeeper是一个分布式的,开源的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。
目前,大部分应用需要开发私有的一个主控、协调器或控制器的协调程序来管理物理分布的子进程(如资源、任务分配等)。而协调程序的反复编写浪费,且难以形成通用、伸缩性好的协调器,所以zookeeper应用而生。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
1. 数据发布与订阅(配置中心) |
---|
发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。例如全局的配置信息,服务式服务框架的服务地址列表等就非常适合使用。应用场景如下:a. 应用中用到的一些配置信息放到ZK上进行集中管理;b. 分布式搜索服务中,索引的元信息和服务器集群机器的节点状态存放在ZK的一些指定节点,供各个客户端订阅使用;c. 分布式日志收集系统;d. 系统中有些信息需要动态获取,并且还会存在人工手动去修改这个信息的发问。 |
2. 负载均衡 |
这里说的负载均衡是指软负载均衡。在分布式环境中,为了保证高可用性,通常同一个应用或同一个服务的提供方都会部署多份,达到对等服务。而消费者就须要在这些对等的服务器中选择一个来执行相关的业务逻辑,其中比较典型的是消息中间件中的生产者,消费者负载均衡。 |
3. 命名服务(Naming Service) |
命名服务也是分布式系统中比较常见的一类场景。在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的机器,提供的服务地址,远程对象等等——这些我们都可以统称他们为名字(Name)。其中较为常见的就是一些分布式服务框架中的服务地址列表。通过调用ZK提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。 |
4. 分布式通知/协调 |
ZooKeeper中特有watcher注册与异步通知机制,能够很好的实现分布式环境下不同系统之间的通知与协调,实现对数据变更的实时处理。使用方法通常是不同系统都对ZK上同一个znode进行注册,监听znode的变化(包括znode本身内容及子节点的),其中一个系统update了znode,那么另一个系统能够收到通知,并作出相应处理。 |
5. 集群管理与Master选举 |
群机器监控:这通常用于那种对集群中机器状态,机器在线率有较高要求的场景,能够快速对集群中机器变化作出响应。这样的场景中,往往有一个监控系统,实时检测集群机器是否存活。 |
6. 分布式锁 |
分布式锁,这个主要得益于ZooKeeper为我们保证了数据的强一致性。锁服务可以分为两类,一个是保持独占,另一个是控制时序。 |
7. 分布式队列 |
队列方面,简单地讲有两种,一种是常规的先进先出队列,另一种是要等到队列成员聚齐之后的才统一按序执行。 |
角色 | 描述 |
---|---|
领导者(Leader) | 领导者负责进行投票的发起和决议,更新系统状态; |
学习者(Learner)_跟随者(Follower) | Follower用于接收客户请求并向客户端返回结果,在选主过程中参与投票; |
学习者(Learner)_观察者(ObServer) | ObServer可以接收客户端连接,将写请求转发给Leader节点;但ObServer不参加投票过程,指同步Leader的状态;ObServer的目的是为了扩展系统,提高读取速度。 |
客户端(Client) | 请求发起方 |
特点 | 说明 |
---|---|
最终一致性 | 为客户端展示同一个视图,这是zookeeper里面一个非常重要的功能; |
可靠性 | 如果消息被到一台服务器接受,那么它将被所有的服务器接受; |
实时性 | Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口; |
独立性 | 各个Client之间互不干预; |
原子性 | 更新只能成功或者失败,没有中间状态; |
顺序性 | 所有Server,同一消息发布顺序一致。 |
准备至少3台Linux虚拟机
通过VMware的克隆虚拟机功能;配置好网络JDK 时间 hosts,保证节点间能互ping通,这里配置了host:node01、node02、node03;
下载安装包,在node01节点下解压
[root@node01 ~]# tar -zxvf zookeeper-3.4.6.tar.gz
[root@node01 usr]# vim zookeeper-3.4.6/conf/zoo.cfg
配置:
tickTime=2000 #发送心跳的间隔时间,单位:毫秒
dataDir=/opt/zookeeperdata
clientPort=2181 #户端连接 Zookeeper 服务器的端口,Zookeeper会监听这个端口,接受客户端的访问请求。
initLimit=5 #初始化连接时最长能忍受多少个心跳时间间隔数 5*2000=10 秒
syncLimit=2 #Leader 与 Follower 之间发送消息,请求和应答时间长度 2*2000=4 秒
server.1=node01:2888:3888
server.2=node02:2888:3888
server.3=node03:2888:3888
在上述配置的dataDir文件夹中创建myid文件,server1机器的内容为:1,server2机器的内容为:2,server3机器的内容为:3;
复制到其他zk节点,记得修改myid。
windows下zookeeper集群搭建请参考:Zookeeper 在Windows下的安装过程及测试。
[root@node01 ~]# vim ~/.bash_profile
在后面添加:
export ZOOKEEPER_HOME=/usr/zookeeper-3.4.6 #这里修改为zookeeper根目录
PATH=$PATH:$ZOOKEEPER_HOME/bin
然后重新加载环境变量:
[root@node01 ~]# source ~/.bash_profile
查看zookeeper环境变量是否配置成功,任意目录下输入z,按Tab键:
[root@node01 ~]# z
zcat zdiff zegrep zforce zic zkCli.cmd zkEnv.cmd zkServer.cmd zless znew
zcmp zdump zfgrep zgrep zkCleanup.sh zkCli.sh zkEnv.sh zkServer.sh zmore zsoelim
会提示出zookeeper的所有指令。
[root@node01 ~]# zkServer.sh start
[root@node01 ~]# zkServer.sh start
JMX enabled by default
Using config: /usr/zookeeper-3.4.6/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@node01 ~]# zkServer.sh status
[root@node01 ~]# zkServer.sh status
JMX enabled by default
Using config: /usr/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: follower #这里显示的状态为跟随者,用于接收客户端请求并向客户端返回结果,在选主过程中参与投票
[root@node01 ~]# zkServer.sh stop
[root@node01 ~]# zkCli.sh
......
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port
[zk: localhost:2181(CONNECTED) 0] ls /
[mynode, zookeeper, hadoop-ha]
[zk: localhost:2181(CONNECTED) 1] ls /zookeeper
[quota]
[zk: localhost:2181(CONNECTED) 2] ls /zookeeper/quota
[]
[zk: localhost:2181(CONNECTED) 3] get /zookeeper #下面空行说明节点内容为空
cZxid = 0x0 #创建节点的id
ctime = Thu Jan 01 08:00:00 CST 1970 #节点的创建时间
mZxid = 0x0 #修改节点的id
mtime = Thu Jan 01 08:00:00 CST 1970 #修改节点的时间
pZxid = 0x0 #子节点的id
cversion = -1 #子节点的版本
dataVersion = 0 #当前节点数据的版本
aclVersion = 0 #权限的版本
ephemeralOwner = 0x0 #判断是否是临时节点
dataLength = 0 #数据的长度
numChildren = 1 #子节点的数量
[zk: localhost:2181(CONNECTED) 3] get /zookeeper #下面空行说明节点内容为空
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
[zk: localhost:2181(CONNECTED) 0] stat /zookeeper
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
[zk: localhost:2181(CONNECTED) 1] ls2 /zookeeper
[quota]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
create [-s] [-e] path data acl #语法格式
[zk: localhost:2181(CONNECTED) 13] create /testnode HelloZookeeper
Created /testnode
[zk: localhost:2181(CONNECTED) 14] get /testnode
HelloZookeeper
cZxid = 0x200000012
ctime = Fri Jan 04 18:55:06 CST 2019
mZxid = 0x200000012
mtime = Fri Jan 04 18:55:06 CST 2019
pZxid = 0x200000012
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0
#创建临时节点
[zk: localhost:2181(CONNECTED) 0] delete /testnode/temp
[zk: localhost:2181(CONNECTED) 1] create -e /testnode/temp tempdata
Created /testnode/temp
#断开连接
[zk: localhost:2181(CONNECTED) 2] quit
Quitting...
2019-01-04 19:00:59,261 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@512] - EventThread shut down
2019-01-04 19:00:59,261 [myid:] - INFO [main:ZooKeeper@684] - Session: 0x1681843584f0007 closed
[root@node01 ~]# zkCli.sh
Connecting to localhost:2181
......
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
#断开重连之后,临时节点自动消失
[zk: localhost:2181(CONNECTED) 0] ls /testnode
[]
# 创建顺序节点,顺序节点会自动累加
[zk: localhost:2181(CONNECTED) 1] create -s /testnode/sec secdata
Created /testnode/sec0000000001
[zk: localhost:2181(CONNECTED) 2] create -s /testnode/sec secdata
Created /testnode/sec0000000002
set path data [version] #语法格式
[zk: localhost:2181(CONNECTED) 9] get /mynode
123456aaaaaa
cZxid = 0x200000002
ctime = Fri Jan 04 17:54:16 CST 2019
mZxid = 0x200000002
mtime = Fri Jan 04 17:54:16 CST 2019
pZxid = 0x20000001f
cversion = 6
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 0
# 修改节点内容为 newdata
[zk: localhost:2181(CONNECTED) 10] set /mynode newdata
cZxid = 0x200000002
ctime = Fri Jan 04 17:54:16 CST 2019
mZxid = 0x200000020
mtime = Fri Jan 04 19:09:13 CST 2019
pZxid = 0x20000001f
cversion = 6
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
#再次查询,节点内容已经修改
[zk: localhost:2181(CONNECTED) 11] get /mynode
newdata
cZxid = 0x200000002
ctime = Fri Jan 04 17:54:16 CST 2019
mZxid = 0x200000020
mtime = Fri Jan 04 19:09:13 CST 2019
pZxid = 0x20000001f
cversion = 6
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
#set 根据版本号更新 dataVersion 乐观锁(注意请从1开始设置)
[zk: localhost:2181(CONNECTED) 15] set /mynode newdata02 1
cZxid = 0x200000002
ctime = Fri Jan 04 17:54:16 CST 2019
mZxid = 0x200000024
mtime = Fri Jan 04 19:12:27 CST 2019
pZxid = 0x20000001f
cversion = 6
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0
#因为数据的版本号已经修改为2 再次使用版本号1修改节点提交错误
[zk: localhost:2181(CONNECTED) 16] set /mynode newdata02 1
version No is not valid : /mynode
delete path [version] #语法格式
[zk: localhost:2181(CONNECTED) 17] delete /testnode
Node not empty: /testnode #拥有子节点,请先删除子节点
[zk: localhost:2181(CONNECTED) 18] delete /testnode/sec0000000001
[zk: localhost:2181(CONNECTED) 19] delete /testnode/sec0000000002
[zk: localhost:2181(CONNECTED) 22] delete /testnode/sec0000000003
[zk: localhost:2181(CONNECTED) 23] delete /testnode
关于watcher机制大体的理解可以为,当每个节点发生变化,都会触发watcher事件,类似于mysql的触发器。zk中 watcher是一次性的,触发后立即销毁。
—stat path [watch] 设置watch事件 ;
[zk: localhost:2181(CONNECTED) 24] stat /testwatch watch
Node does not exist: /testwatch
[zk: localhost:2181(CONNECTED) 25] create /testwatch test
WATCHER::
WatchedEvent state:SyncConnected type:NodeCreated path:/testwatch
Created /testwatch
—get path [watch]设置watch事件 ;
[zk: localhost:2181(CONNECTED) 27] create /testwatch2 aaa
Created /testwatch2
#使用get命令添加watch事件
[zk: localhost:2181(CONNECTED) 28] get /testwatch2 watch
aaa
cZxid = 0x20000002d
ctime = Fri Jan 04 19:25:44 CST 2019
mZxid = 0x20000002d
mtime = Fri Jan 04 19:25:44 CST 2019
pZxid = 0x20000002d
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
#修改节点触发watcher事件
[zk: localhost:2181(CONNECTED) 29] set /testwatch2 newdata
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/testwatch2
cZxid = 0x20000002d
ctime = Fri Jan 04 19:25:44 CST 2019
mZxid = 0x20000002e
mtime = Fri Jan 04 19:26:31 CST 2019
pZxid = 0x20000002d
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
#注:再次修改节点无法触发watcher时间,需要重新get /testwatch2 watch 添加触发。
—子节点创建和删除时触发watch事件,子节点修改不会触发该事件;
ZK的节点有5种操作权限:CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,这5种权限简写为crwda(即:每个单词的首字符缩写)。
注:这5种权限中,delete是指对子节点的删除权限,其它4种权限指对自身节点的操作权限
身份的认证有4种方式:
nc
命令,(yum install nc
);[root@node01 ~]# echo stat | nc 192.168.142.11 2181
Zookeeper version: 3.4.6-1569965, built on 02/20/2014 09:09 GMT
Clients:
/192.168.142.11:43669[0](queued=0,recved=1,sent=0)
Latency min/avg/max: 0/1/119
Received: 552
Sent: 554
Connections: 1
Outstanding: 0
Zxid: 0x200000034
Mode: follower
Node count: 10
[root@node01 ~]# echo ruok | nc 192.168.142.11 2181
imok[root@node01 ~]#
[root@node01 ~]# echo dump | nc 192.168.142.11 2181
SessionTracker dump:
org.apache.zookeeper.server.quorum.LearnerSessionTracker@44fc7eee
ephemeral nodes dump:
Sessions with Ephemerals (0):
[root@node01 ~]# echo conf | nc 192.168.142.11 2181
clientPort=2181
dataDir=/opt/zookeeper/version-2
dataLogDir=/opt/zookeeper/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=1
initLimit=10
syncLimit=5
electionAlg=3
electionPort=3888
quorumPort=2888
peerType=0
[root@node01 ~]# echo cons | nc 192.168.142.11 2181
/192.168.142.11:43674[0](queued=0,recved=1,sent=0)
[root@node01 ~]# echo envi | nc 192.168.142.11 2181
Environment:
zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT
host.name=node01
java.version=1.8.0_191
java.vendor=Oracle Corporation
java.home=/usr/java/jdk1.8.0_191-amd64/jre
java.class.path=/usr/zookeeper-3.4.6/bin/../build/classes:/usr/zookeeper-3.4.6/bin/../build/lib/*.jar:/usr/zookeeper-3.4.6/bin/../lib/slf4j-log4j12-1.6.1.jar:/usr/zookeeper-3.4.6/bin/../lib/slf4j-api-1.6.1.jar:/usr/zookeeper-3.4.6/bin/../lib/netty-3.7.0.Final.jar:/usr/zookeeper-3.4.6/bin/../lib/log4j-1.2.16.jar:/usr/zookeeper-3.4.6/bin/../lib/jline-0.9.94.jar:/usr/zookeeper-3.4.6/bin/../zookeeper-3.4.6.jar:/usr/zookeeper-3.4.6/bin/../src/java/lib/*.jar:/usr/zookeeper-3.4.6/bin/../conf:
java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
java.io.tmpdir=/tmp
java.compiler=
os.name=Linux
os.arch=amd64
os.version=2.6.32-504.el6.x86_64
user.name=root
user.home=/root
user.dir=/opt/hadoop/dfs
[root@node01 ~]# echo mntr | nc 192.168.142.11 2181
zk_version 3.4.6-1569965, built on 02/20/2014 09:09 GMT
zk_avg_latency 1
zk_max_latency 119
zk_min_latency 0
zk_packets_received 559
zk_packets_sent 561
zk_num_alive_connections 1
zk_outstanding_requests 0
zk_server_state follower
zk_znode_count 10
zk_watch_count 0
zk_ephemerals_count 0
zk_approximate_data_size 154
zk_open_file_descriptor_count 30
zk_max_file_descriptor_count 4096
[root@node01 ~]# echo wchs | nc 192.168.142.11 2181
0 connections watching 0 paths
Total watches:0
wchc和wchp 显示session的watch信息 path的watch信息,需要在 配置zoo.cfg
文件中添加 4lw.commands.whitelist=*。