Zookeeper基础知识与部署

一、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节点的应答后,才会执行真正的“事务操作”

image.png

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集群式相当不利的。

        为了避免“惊群效应”,我们在实现“分布式锁”时,选用“临时有序节点”的方式实现分布式锁,没获得到锁的线程,就创建一个新的“临时顺序节点”,并监听它的前一个节点,这样串联起来,每次只会有一个线程被唤醒,就避免了“惊群效应”,这种方式实现的分布式锁为公平锁,因为“先到先得”

jiguiquan@163.com

文章作者信息...

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐