一、Zookeeper的基础知识与特性
-
Zookeeper是一个分布式协调框架,主要用来解决分布式应用中一些集群数据管理问题:
-
分布式锁(基于容器节点+临时有序节点+监听机制)
-
分布式协调(也是基于分布式锁)
-
注册中心(基于虚拟节点+监听机制)
-
Zookeeper维护了一个类似Linux文件系统的数据结构,根目录为"/"
-
Zookeeper的2大基础特性:6大节点类型 + 监听机制 + [ACL]
1、六大节点类型:
//创建命令: create [-s] [-e] [-c] [-t ttl] path [data] [acl] //理解了创建命令,也就理解了6大节点类型
-
Persistent:持久化节点,默认创建即为持久化节点;
-
Persistent_Sequential:持久化有序节点:-s
-
Ephemeral:临时节点:-e
-
Ephemeral_Sequential:临时有序节点:-e -s
-
Container:容器节点,特点是当节点下所有的子节点都被删除后,容器节点将会被系统自动清除,可用于实现分布式锁,当所有的监听子节点都被释放掉后,锁目录也可以直接被删除,便于维护;
-
TTL:TTL节点:[-t ttl],当ttl时间到达后,该节点就会被系统自动清除;必须通过启动参数:zookeeper.extendedTypesEnabled=true开启;
Container节点和TTL节点不是立即删除,而且被系统定时任务默认每隔60s检查清除一次!
2、监听机制:
客户端注册监听它关心的任意节点、目录、以及递归子目录:所有的通知都是一次性的:-w
监听指定节点:get -w /jiguiquan :当该节点被更新时,会收到通知 监听指定目录:ls -w /jiguiquan :当该目录下的子节点发生变化时,会收到通知 监听指定目录及递归监听其下所有目录:ls -w -R /jiguiquan :递归该目录下所有目录,然后依次监听 # 所有的监听都是一次性的!
二、Zookeeper单机版的安装
Zookeeper时基于Java环境的,所以,先安装Java环境:https://www.jiguiquan.com/?p=273
1、下载Zookeeper安装文件:
# 阿里镜像源:http://mirrors.aliyun.com/apache/zookeeper/zookeeper-3.5.10/apache-zookeeper-3.5.10-bin.tar.gz # 清华镜像源:https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.5.10/apache-zookeeper-3.5.10-bin.tar.gz # 北大镜像源:https://mirrors.pku.edu.cn/apache/zookeeper/zookeeper-3.5.10/apache-zookeeper-3.5.10-bin.tar.gz wget http://mirrors.aliyun.com/apache/zookeeper/zookeeper-3.5.10/apache-zookeeper-3.5.10-bin.tar.gz tar -zxvf apache-zookeeper-3.5.10-bin.tar.gz mv apache-zookeeper-3.5.10-bin /usr/local/zookeeper [root@jiguiquan ~]# cd /usr/local/zookeeper/ [root@jiguiquan zookeeper]# ls bin conf docs lib LICENSE.txt NOTICE.txt README.md README_packaging.txt
2、重命名并修改配置文件信息:
[root@jiguiquan ~]# cd /usr/local/zookeeper/ [root@jiguiquan zookeeper]# cp conf/zoo_sample.cfg conf/zoo.cfg [root@jiguiquan zookeeper]# vim conf/zoo.cfg tickTime=2000 # 心跳间隔时间 initLimit=10 # LF初始通讯时限(tickTime的倍数),Follower连接并从Leader同步数据的允许时间,如果数据量很大,可适当调大此参数; syncLimit=5 # LF同步通讯时限(tickTime的倍数),Leader复杂整个ZK集群的维护,通过心跳判断Follower是否存活等,不要设置太大,否则可能会掩盖一些问题! dataDir=/data/zookeeper # 修改数据存放位置(包含Log日志文件 + Snapshot快照文件) clientPort=2181 # 修改zookeeper启动端口,客户端连接时用的端口
3、启动Zookeeper:
[root@jiguiquan local]# cd zookeeper/ [root@jiguiquan zookeeper]# ls bin conf docs lib LICENSE.txt NOTICE.txt README.md README_packaging.txt [root@jiguiquan zookeeper]# ls bin/ README.txt zkCleanup.sh zkCli.cmd zkCli.sh zkEnv.cmd zkEnv.sh zkServer.cmd zkServer-initialize.sh zkServer.sh zkTxnLogToolkit.cmd zkTxnLogToolkit.sh [root@jiguiquan zookeeper]# bin/zkServer.sh start conf/zoo.cfg ZooKeeper JMX enabled by default Using config: conf/zoo.cfg Starting zookeeper ... STARTED # 对应的停止命令: bin/zkServer.sh stop
4、使用客户端工具连接Zookeeper:
bin/zkCli.sh -server 192.168.121.121:2181 # 如果是本机,且为2181默认端口时,直接: bin/zkCli.sh
5、zkCli连接后的常用命令:
ls [‐s] [‐w] [‐R] path # 查看目录下的节点 create [‐s] [‐e] [‐c] [‐t ttl] path [data] [acl] # 创建各类节点 delete [‐v version] path # 删除节点 deleteall path # 删除目录节点及其下的所有节点 get [‐s] [‐w] path # 监听指定节点 ls [‐s] [‐w] [‐R] path # 监听指定目录,-R递归监听 removewatches path # 移除指定节点上的所有监听器 stat [‐w] path # 查看节点状态 [zk: localhost:2181(CONNECTED) 5] stat /jiguiquan cZxid = 0x1b ## 创建时的事务ID ctime = Mon Jul 04 02:48:26 UTC 2022 ## 创建时间 mZxid = 0x1d ## 更新时的事务ID mtime = Mon Jul 04 02:48:51 UTC 2022 ## 最后更新时间 pZxid = 0x1b cversion = 0 dataVersion = 2 ## 数据版本号,被修改过几次,从0开始 aclVersion = 0 ephemeralOwner = 0x100003def960001 ## 虚拟节点所属sessionId,持久化节点此值为0x0 dataLength = 3 ## 数据长度 numChildren = 0 ## 直属子节点个数,注意:虚拟机点不允许创建子节点 quit # 退出zkCli客户端
三、Zookeeper集群版安装(单机模拟)
首先,弄清楚,Zookeeper集群节点供分为三类:Leader + Follower + Observer
Leader:处理所有事务请求(写请求)
Follower:只能处理读请求,参与集群选主 以及 Leader节点写数据时的过半判断;
Observer:只能处理读请求,不参与集群选主 以及 Leader节点写数据时的过半判断,因此可以在增加集群读能力的同时,减少Leader节点写请求的压力!
所谓“Leader节点写数据时的过半判断”:Leader节点接收到事务指令时,除了“记录事务日志”外,还要将此“事务日志”同步到其它Follower节点,直到收到过半的Follower节点的应答后,才会执行真正的“事务操作”!
1、创建4个数据目录,并在每个数据目录下创建一个myid文件:
[root@jiguiquan zookeeper]# cd /data/ [root@jiguiquan data]# mkdir zk1 zk2 zk3 zk4 [root@jiguiquan data]# ls zk1 zk2 zk3 zk4 zookeeper # 定义每个节点的唯一id [root@jiguiquan data]# echo 1 > zk1/myid [root@jiguiquan data]# echo 2 > zk2/myid [root@jiguiquan data]# echo 3 > zk3/myid [root@jiguiquan data]# echo 4 > zk4/myid
2、将原配置文件复制4份:
[root@jiguiquan conf]# cp zoo_sample.cfg zoo1.cfg [root@jiguiquan conf]# cp zoo1.cfg zoo2.cfg [root@jiguiquan conf]# cp zoo1.cfg zoo3.cfg [root@jiguiquan conf]# cp zoo1.cfg zoo4.cfg [root@jiguiquan conf]# ls configuration.xsl log4j.properties zoo1.cfg zoo2.cfg zoo3.cfg zoo4.cfg zoo.cfg zoo_sample.cfg
配置文件详情:
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zk1 # 指定对应的数据存放目录 clientPort=2181 # 指定对应的节点端口 # 集群配置:server.A=B:C:D:E ## A:节点编号,与myid一致 ## B:IP ## C:与Leader节点的通讯端口(默认2888) ## D:选举端口(默认3888) ## E:节点类型,participant:参与选举,observer:不参与选举的观察者 server.1=192.168.121.121:2001:3001:participant server.2=192.168.121.121:2002:3002:participant server.3=192.168.121.121:2003:3003:participant server.4=192.168.121.121:2004:3004:observer
3、使用 zkServer.sh 分别启动四个节点:
[root@jiguiquan zookeeper]# bin/zkServer.sh start conf/zoo1.cfg ZooKeeper JMX enabled by default Using config: conf/zoo1.cfg Starting zookeeper ... STARTED [root@jiguiquan zookeeper]# bin/zkServer.sh start conf/zoo2.cfg ZooKeeper JMX enabled by default Using config: conf/zoo2.cfg Starting zookeeper ... STARTED [root@jiguiquan zookeeper]# bin/zkServer.sh start conf/zoo3.cfg ZooKeeper JMX enabled by default Using config: conf/zoo3.cfg Starting zookeeper ... STARTED [root@jiguiquan zookeeper]# bin/zkServer.sh start conf/zoo4.cfg ZooKeeper JMX enabled by default Using config: conf/zoo4.cfg Starting zookeeper ... STARTED # 查看当前Java进程: [root@jiguiquan zookeeper]# jps 23063 Jps 22825 QuorumPeerMain 22877 QuorumPeerMain 22943 QuorumPeerMain 23007 QuorumPeerMain
4、查看每个节点的状态:
[root@jiguiquan zookeeper]# bin/zkServer.sh status conf/zoo1.cfg ZooKeeper JMX enabled by default Using config: conf/zoo1.cfg Client port found: 2181. Client address: localhost. Client SSL: false. Mode: follower [root@jiguiquan zookeeper]# bin/zkServer.sh status conf/zoo2.cfg ZooKeeper JMX enabled by default Using config: conf/zoo2.cfg Client port found: 2182. Client address: localhost. Client SSL: false. Mode: leader [root@jiguiquan zookeeper]# bin/zkServer.sh status conf/zoo3.cfg ZooKeeper JMX enabled by default Using config: conf/zoo3.cfg Client port found: 2183. Client address: localhost. Client SSL: false. Mode: follower [root@jiguiquan zookeeper]# bin/zkServer.sh status conf/zoo4.cfg ZooKeeper JMX enabled by default Using config: conf/zoo4.cfg Client port found: 2184. Client address: localhost. Client SSL: false. Mode: observer
5、使用zkCli连接集群:
# -server 传入所有节点,zk客户端会通过随机算法,随便选择一台进行连接: [root@jiguiquan zookeeper]# bin/zkCli.sh -server 192.168.121.121:2181,192.168.121.121:2182,192.168.121.121:2183,192.168.121.121:2184 ... ... Session establishment complete on server 192.168.121.121/192.168.121.121:2184, sessionid = 0x40000a5f1760000, negotiated timeout = 30000 WATCHER:: WatchedEvent state:SyncConnected type:None path:null [zk: 192.168.121.121:2181,192.168.121.121:2182,192.168.121.121:2183,192.168.121.121:2184(CONNECTED) 0] / /zookeeper /zookeeper/config /zookeeper/quota ## 连接集群时,我们可以选择传入所有IP:Port,也可以只传入指定的一台 ### 所有IP:Port 断开后可以重连到其它节点,更健壮! ### 指定IP:Port 断开就断开了!
四、Zookeeper的ACL(权限控制)
Zookeeper的ACL权限控制,可以控制节点的读写权限、保证数据的安全性;
权限操作命令由 3 部分构成“scheme:id:permission”:权限模式(Scheme)、授权对象(ID)、权限信息(Permission)
权限模式(Scheme):
范围验证:对“单个IP或者IP段”进行授权:ip:192.168.121.121 或者 ip:192.168.121.1/24
口令验证:digest:用户名:加密后的密码,另外还有auth模式,为明文验证!与digest类似;
super:超级管理员
授权对象(ID):可以是IP、IP段、用户名:密码,如果时world模式,则指的是系统中所有的用户world:anyone
权限信息(Permission):crwda
c:create,可以在授权对象下创建子节点
w:write,可以更新该节点
r:read,可以读取该节点信息 + 子节点列表信息
d:delete,可以删除该节点以及它的子节点
a:admin,可以修改该节点的ACL权限
1、ACL常用命令:
getAcl: 获取ACL信息 setAcl: 设置ACL信息 addauth: 输入认证授权信息,相当于登录
如果设置zookeeper.skipACL=yes,那么所有的ACL设置全部失效,被跳过!
2、如何生成密钥ID:
-
通过java代码生成:
@Test public void generateSuperDigest() throws NoSuchAlgorithmException { String sId = DigestAuthenticationProvider.generateDigest("jiguiquan:123456"); System.out.println(sId);// jiguiquan:3vyfkbhCKrt4jqEIILDxj5FqGyg= }
-
通过Linux命令生成:
echo -n <user>:<123456> | openssl dgst -binary -sha1 | openssl base64 # 生成: [root@jiguiquan zookeeper]# echo -n admin:123456 | openssl dgst -binary -sha1 | openssl base64 0uek/hZ/V9fgiM35b0Z2226acMQ=
3、设置ACL权限控制
创建节点的同时设置ACL(create…)
# 创建jiguiquan节点 [zk: 192.168.121.121:2181(CONNECTED) 2] create /jiguiquan jidata digest:admin:0uek/hZ/V9fgiM35b0Z2226acMQ=:crwda # 查看信息,可见均无权限: [zk: 192.168.121.121:2181(CONNECTED) 4] getAcl /jiguiquan Authentication is not valid : /jiguiquan [zk: 192.168.121.121:2181(CONNECTED) 5] get /jiguiquan org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /jiguiquan # 输入认证信息,完成登录 [zk: 192.168.121.121:2181(CONNECTED) 6] addauth digest admin:123456 [zk: 192.168.121.121:2181(CONNECTED) 7] get /jiguiquan jidata [zk: 192.168.121.121:2181(CONNECTED) 8] getAcl /jiguiquan 'digest,'admin:0uek/hZ/V9fgiM35b0Z2226acMQ= : cdrwa
修改节点的ACL权限(setAcl…):
[zk: 192.168.121.121:2181(CONNECTED) 9] setAcl /jiguiquan digest:admin:0uek/hZ/V9fgiM35b0Z2226acMQ=:rw [zk: 192.168.121.121:2181(CONNECTED) 10] get /jiguiquan jidata [zk: 192.168.121.121:2181(CONNECTED) 11] getAcl /jiguiquan 'digest,'admin:x : rw
设置IP模式的ACL权限:
# 分别设置本机IP和其它IP两个节点资源 [zk: 192.168.121.121:2181(CONNECTED) 12] create /jip testip ip:192.168.121.121:crwda Created /jip [zk: 192.168.121.121:2181(CONNECTED) 13] create /jip-other otherip ip:192.168.121.122:crwda Created /jip-other # 本机IP的节点可以访问,其它IP的节点无法访问 [zk: 192.168.121.121:2181(CONNECTED) 14] get /jip testip [zk: 192.168.121.121:2181(CONNECTED) 15] get /jip-other org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /jip-other
4、getAcl 信息详解:
[zk: 192.168.121.121:2181,192.168.121.121:2182,192.168.121.121:2183,192.168.121.121:2184(CONNECTED) 25] getAcl /jiguiquan 'digest,'admin:0uek/hZ/V9fgiM35b0Z2226acMQ= # 权限模式为digest,后面是验证信息 : cdrwa # 具体权限 # 无Acl权限控制的节点 [zk: 192.168.121.121:2181,192.168.121.121:2182,192.168.121.121:2183,192.168.121.121:2184(CONNECTED) 26] getAcl /zookeeper 'world,'anyone # 对于没有添加Acl控制的节点,为world:anyone : cdrwa # 去除节点Acl权限 [zk: 192.168.121.121:2181,192.168.121.121:2182,192.168.121.121:2183,192.168.121.121:2184(CONNECTED) 33] setAcl /jiguiquan world:anyone:crwda [zk: 192.168.121.121:2181,192.168.121.121:2182,192.168.121.121:2183,192.168.121.121:2184(CONNECTED) 34] getAcl /jiguiquan 'world,'anyone : cdrwa
5、Super超级管理员模式:
当我们忘记了某个节点的密钥,此时我们可以通过使用super管理员模式,在此模式下,可以对所有的节点进行任意的操作!
# 需要修改Zookeeper的jvm启动参数: -Dzookeeper.DigestAuthenticationProvider.superDigest=super:<base64encoded(SHA1(password)) # 之后,我们就可以在需要的时候,登录super账号对节点进行管理了!
五、Zookeeper的数据与持久化
# 根据源码可知,Zookeeper的数据结构为ConcurrentHashMap ## key为:节点路径 /jiguiquan ## value为:DataNode public class DataTree { private final ConcurrentHashMap<String, DataNode> nodes = new ConcurrentHashMap<String, DataNode>(); private final WatchManager dataWatches = new WatchManager(); private final WatchManager childWatches = new WatchManager(); } # 每一个DataNode中包含 ## byte[] 字节数组 ## stat 状态信息 ## children:子节点路径 public class DataNode implements Record { byte data[]; Long acl; public StatPersisted stat; private Set<String> children = null; }
1、Zokkeeper的数据存放在配置文件中指定的目录下:
[root@jiguiquan data]# ll /data/zookeeper/version-2/ total 12 -rw-r--r--. 1 root root 67108880 Jul 4 03:08 log.1 # 事务日志文件 -rw-r--r--. 1 root root 424 Jul 4 02:31 snapshot.0 # 快照文件
2、Zookeeper的数据文件,有点类似于redis:
-
详细的事务日志文件,可以确保数据的完整性;(类似于 Redis 中 AOF 持久化文件)
-
快照文件,可以提高zookeeper数据恢复是的速度;(类似于 Redis 中 RDB 持久化文件)
六、Zookeeper的集群效应
所有“惊群效应”,又称为“羊群效应”:由于Zookeeper存在监听机制,最典型的应用场景就是“分布式锁”,当尝试获得锁的线程没有获得到“/lock”锁,就会对“/lock”节点进行监听,但是当竞争很大时,会有大量的线程对“/lock”节点进行监听,当某一时刻“/lock”锁被释放时,会一瞬间唤醒其它所有监听中的线程,这对zookeeper集群式相当不利的。
为了避免“惊群效应”,我们在实现“分布式锁”时,选用“临时有序节点”的方式实现分布式锁,没获得到锁的线程,就创建一个新的“临时顺序节点”,并监听它的前一个节点,这样串联起来,每次只会有一个线程被唤醒,就避免了“惊群效应”,这种方式实现的分布式锁为公平锁,因为“先到先得”。