Zookeeper
简介/快速入门
ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable. Because of the difficulty of implementing these kinds of services, applications initially usually skimp on them, which make them brittle in the presence of change and difficult to manage. Even when done correctly, different implementations of these services lead to management complexity when the applications are deployed.
ZooKeeper
用于维护配置信息、命名、提供分布式同步和提供组服务的集中服务。所有这些类型的服务都以某种形式被分布式应用程序使用。每次实现,都会有大量的工作来修复不可避免的错误和竞争条件。由于实现这些服务的困难,应用程序最初通常会忽略这些服务,这使得它们变得脆弱和难以管理。即使这些服务得到了正确的实施,这些服务的不同实现也会导致管理的复杂性
zookeeper
由雅虎研究院开发,由雅虎研究院开发 Google Chubby
实现开源,后来托管 Apache
,于2010年11月
正式成为apache
的顶级项目
在大数据生态系统中,许多组件被命名为一些动物或昆虫,如hadoop
大象,hive
就是蜂巢,zookeeper
即管理员,顾名思义,即使管理大数据生态系统各组件的管理员,如下所示:
[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-HyFodBZ7-1657433838494)(assets/zookeeper-1.png)]
应用场景
zookeepepr
是经典数据一致性解决方案致力于为分布式应用程序提供高性能、高可用性和严格顺序访问控制的分布式协调存储服务。
-
-
java
编程经常遇到配置项,如数据库url
、schema
、user
和password
等待。通常,我们将这些配置项放置在配置文件中,然后将配置文件放置在服务器上。当需要更改配置项时,我们需要在服务器上修改相应的配置文件。然而,随着分布式系统的兴起,由于配置文件需要使用许多服务,因此存在(
highavailability
)配置数据与各服务器一致。配置文件通常部署在一个集群上,但是一个此时,如果服务器逐个修改配置文件,将是一个非常繁琐和危险的操作,因此,,并能保证每个服务器上配置项的数据一致性。
,其使用
Zab
这种一致性协议保证了一致性。现在使用了许多开源项目。zookeeper
维护配置,如在hbase
客户端是连接一个zookeeper
,获得必要的hbase
在进一步操作之前,集群配置信息。还有开源消息队列kafka
中,也便用zookeeper
来维护brokers
的信息。在alibaba
开源的soa
框架dubbo
它也被广泛使用zookeeper
实现服务治理来实现服务治理。[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-fbAGRYnH-1657433838496)(assets/zookeeper-2.png)]
-
-
分布式锁服务
- 一个集群是一个由多个服务器组成的分布式系统。为了提高并发性和可靠性,多个服务器运行相同的服务。当多个服务运行时,需要协调每个服务的进度,有时需要确保当一个服务运行时,其他服务不能进行操作,即锁定操作,如果当前机器挂断,释放锁
fail over
继续执行服务到其他机器
- 一个集群是一个由多个服务器组成的分布式系统。为了提高并发性和可靠性,多个服务器运行相同的服务。当多个服务运行时,需要协调每个服务的进度,有时需要确保当一个服务运行时,其他服务不能进行操作,即锁定操作,如果当前机器挂断,释放锁
-
集群管理
-
由于各种软硬件故障或网络故障,一个集群有时会被移除,而一些服务器被添加到集群中,
zookeeper
通知集群中其他正常工作的服务器,并及时调整存储和计算任务的分配和执行。zookeeper
对故障服务器进行诊断并尝试修复。[外链图片存储失败,源站可能有防盗链机制,建议保存图片直接上传(img-nJk3uwG1-1657433838497)(assets/zookeeper-3.png)]
-
-
唯一的生产分布式ID
-
在过去的单库单表系统中,通常使用数据库字段
auto_ increment
属性自动生成每个记录的唯一属性ID
。但是分库分表后,依靠数据库是不可能的auto_ Increment
属性是唯一标记的记录。此时,我们可以使用它zookeeper
在分布式环境下生成全局ID
。做法如下:每次要生成一个新
id
创建一个持久的顺序节点,创建操作返回的节点序号,即新的id
,然后删除比自己节点小的节点
-
Zookeeper的设计目标
zooKeeper
致力于为分布式应用提供高性能、高可用性、严格顺序访问控制的分布式协调服务
- 高性能
zookeeper
存储全量数据直接服务于客户端的所有非事务请求,特别是阅读应用场景
- 高可用
zookeeper
一般集群提供外部服务,一般3~5
台机可以形成可用的台机Zookeeper
当集群结束时,每台机器将在内存中保持当前的服务器状态,并保持每台机器之间的通信。只要集群中一半以上的机器能够正常工作,整个集群就能够正常提供外部服务
- 严格顺序访问
- 对客户端的每个更新请求,
Zookeeper
全球唯一的递增编号将被分配,反映了所有事务操作的顺序
- 对客户端的每个更新请求,
数据模型
zookeeper
数据结点可视为树状结构(或目录),树中的每个结点称为znode
(即zookeeper node
),一个znode
由多个子结点组成。zookeeper
结点在结构上呈树状;
使用路径path
来定位某个znode
,比如/ns-1/itcast/mysqml/schemal1/table1
,此处ns-1,itcast、mysql、schemal1、table1根结点、2级结点、3级结点以及4级结点
;其中ns-1
是itcast
的父结点,itcast
是ns-1
的子结点,itcast
是mysql
的父结点…以此类推
znode
,间距文件和目录两种特点,即像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kg7fbtk7-1657433838498)(assets/zookeeper-4.png)]
那么如何描述一个znode
呢?一个znode
大体上分为3
个部分:
- 结点的数据:即
znode data
(结点path
,结点data
)的关系就像是Java map
中的key value
关系 - 结点的子结点
children
- 结点的状态
stat
:用来描述当前结点的创建、修改记录,包括cZxid
、ctime
等
结点状态stat的属性
在zookeeper shell
中使用 get
命令查看指定路径结点的data
、stat
信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-omwEyzAm-1657433838499)(assets/zookeeper-5.png)]
属性说明:
结点的各个属性如下。其中重要的概念是Zxid(Zookeeper Transaction ID)
,Zookeeper
结点的每一次更改都具有唯一的Zxid
,如果Zxid-1
小于 Zxid-2
,则Zxid-1
的更改发生在 Zxid-2
更改之前
https://zookeeper.apache.org/doc/r3.4.14/zookeeperProgrammers.html#sc_zkDataModel_znodes
cZxid
数据结点创建时的事务ID——针对于zookeeper
数据结点的管理:我们对结点数据的一些写操作都会导致zookeeper
自动地为我们去开启一个事务,并且自动地去为每一个事务维护一个事务ID
ctime
数据结点创建时的时间mZxid
数据结点最后一次更新时的事务IDmtime
数据结点最后一次更新时的时间pZxid
数据节点最后一次修改此znode
子节点更改的zxid
cversion
子结点的更改次数dataVersion
结点数据的更改次数aclVersion
结点的ACL更改次数——类似linux
的权限列表,维护的是当前结点的权限列表被修改的次数ephemeralOwner
如果结点是临时结点,则表示创建该结点的会话的SessionID
;如果是持久结点,该属性值为0dataLength
数据内容的长度numChildren
数据结点当前的子结点个数
zookeeper
中的结点有两种,分别为和。结点的类型在创建时被确定,并且不能改变
- 临时节点:
- 该节点的生命周期依赖于创建它们的会话。一旦会话(
Session
)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的Znode
都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,Zookeeper
的临时节点不允许拥有子节点
- 该节点的生命周期依赖于创建它们的会话。一旦会话(
- 持久化结点:
- 该结点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,它们才能被删除
单机安装
测试系统环境centos7.3
zookeeper:zookeeper-3.4.10.tar.gz
jdk:jdk-8u131-linux-x64.tar.gz
http://archive.apache.org/dist/zookeeper/
-
在
centos
中使用root
用户创建zookeeper
用户,用户名:zookeeper
密码:zookeeper
-
useradd zookeeper passwd zookeeper su zookeeper
-
-
zookeeper
底层依赖于jdk,zookeeper
用户登录后,根目录下先进行jdk 的安装,jdk使用jdk-8u131-linux-x64.tar.gz
-
tar -zxf tar.gz
-
-
配置jdk 环境变量
-
vi /etc/profile JAVA_HOME=/home/zookeeper/jdk1.8.0_131 export JAVA_HOME PATH=$JAVA_HOME/bin:$PATH export PATH souce /etc/profile
-
-
检测jdk安装
java -version
// 如果反馈了Java信息,则成功
-
zookeeper
上传解压tar -zxf tar.gz
-
为
zookeeper
准备配置文件-
# 进入conf目录 cd /home/zookeeper/zookeeper-3.4.10/conf # 复制配置文件 cp zoo_sampe.cfg zoo.cfg # zookeeper 根目录下创建data目录 mkdir data # vi 配置文件中的dataDir # 此路径用于存储zookeeper中数据的内存快照、及事务日志文件,虽然zookeeper是使用内存的,但是需要持久化一些数据来保证数据的安全,和redis一样 dataDir=/home/zookeeper/zookeeper-3.4.10/data
-
-
启动
zookeeper
-
# 进入zookeeper的bin目录 cd /home/zookeeper/zookeeper-3.4.10/bin # 启动zookeeper ./zkServer.sh start # 启动: zkServer.sh start # 停止: zkServer.sh stop # 查看状态:zkServer.sh status # 进入zookeeper 内部 ./zkCli.sh
-
常用shell命令
zookeeper
——getting started
——https://zookeeper.apache.org/doc/r3.4.14/zookeeperStarted.html#sc_FileManagement
操作结点
get /hadoop
查看结点的数据和属性 stat /hadoop
查看结点的属性
创建结点并写入数据:
create [-s] [-e] path data
# 其中 -s 为有序结点,-e 临时结点(默认是持久结点)
create /hadoop "123456" # 此时,如果quit退出后再./ZkCient.sh 登入
# 再用输入 get /hadoop 获取,结点依然存在(永久结点)
create -s /a "a" # 创建一个持久化有序结点,创建的时候可以观察到返回的数据带上了一个id
create -s /b "b" # 返回的值,id递增了
create -s -e /aa "aa" # 依然还会返回自增的id,quit后再进来,继续创建,id依然是往后推的
create /aa/xx # 继续创建结点,可以看到pZxid变化了
更新结点的命令是set
,可以直接进行修改,如下:
set path [version]
set /hadoop "345" # 修改结点值
set /hadoop "hadoop-x" 1 # 也可以基于版本号进行更改,类似于乐观锁,当传入版本号(dataVersion)
# 和当前结点的数据版本号不一致时,zookeeper会拒绝本次修改
删除结点的语法如下:
delete path [version]
和 set
方法相似,也可以传入版本号
delete /hadoop # 删除结点
delete /hadoop 1 # 乐观锁机制,与set 方法一致
要想删除某个结点及其所有后代结点,可以使用递归删除,命令为 rmr path
ls /hadoop # 可以查看结点的列表
ls2 /hadoop # 可以查看结点的列表以及目标结点的信息
ls / # 根节点
使用get path [watch]
注册的监听器能够在结点的时候,向客户端发出通知。需要注意的是zookeeper
的触发器是一次性的(One-time trigger
),即触发一次后就会立即失效
get /hadoop watch # get 的时候添加监听器,当值改变的时候,监听器返回消息
set /hadoop 45678 # 测试
使用 ls path [watch] 或 ls2 path [watch]
注册的监听器能够监听该结点下的和操作
ls /hadoop watch # 添加监听器
set /hadoop/node "node"
zookeeper的Acl权限控制
https://zookeeper.apache.org/doc/r3.4.14/zookeeperProgrammers.html#sc_ZooKeeperAccessControl
zookeeper
类似文件系统,client
可以创建结点、更新结点、删除结点,那么如何做到结点的权限控制呢?
zookeeper
的 access control list
访问控制列表可以做到这一点
acl
权限控制,使用scheme:id:permission
来标识,主要涵盖3个方面:
https://zookeeper.apache.org/doc/r3.4.14/zookeeperProgrammers.html#sc_BuiltinACLSchemes
- 权限模式(
scheme
):授权的策略 - 授权对象(
id
):授权的对象 - 权限(
permission
):授予的权限
其特性如下:
-
zookeeper
的权限控制是基于每个znode
结点的,需要对每个结点设置权限 -
每个
znode
支持多种权限控制方案和多个权限 -
子结点不会继承父结点的权限,客户端无权访问某结点,但可能可以访问它的子结点:
例如
setAcl /test2 ip:192.168.133.133:crwda
// 将结点权限设置为Ip:192.168.133.133 的客户端可以对节点进行 增删改查和管理权限
-
采用何种方式授权
-
方案 描述 world 只有一个用户: anyone
,代表登录zookeeper
所有人(默认)ip 对客户端使用IP地址认证 auth 使用已添加认证的用户认证 digest 使用"用户名:密码"方式认证
- 给谁授予权限
- 授权对象ID是指,权限赋予的实体,例如:IP地址或用户
-
授予什么权限
-
create、delete、read、writer、admin
也就是 增、删、查、改、管理权限,这5种权限简写为 c d r w a,注意: 这五种权限中,有的权限并不是对结点自身操作的例如:delete是指对的删除权限可以试图删除父结点,但是子结点必须删除干净,所以
delete
的权限也是很有用的 -
权限 ACL简写 描述 create c 可以创建子结点 delete d 可以删除子结点(仅下一级结点) read r 可以读取结点数据以及显示子结点列表 write w 可以设置结点数据 admin a 可以设置结点访问控制权限列表
-
命令 使用方式 描述 getAcl getAcl 读取ACL权限 setAcl setAcl 设置ACL权限 addauth addauth 添加认证用户
案例/远程登录
可以远程登录
getAcl /node
// 读取权限信息setAcl /node world:anyone:drwa
// 设置权限(禁用创建子结点的权限)
./zkServer.sh -server 192.168.133.133
可以远程登录
setAcl /hadoop ip:192.168.133.133:drwa
- 如果在两台不同的虚拟机中,另一台用远程连接的模式,进行上面这条命令,那么只会有一台被授权
- 需要两台虚拟机一起授权的话需要用将授权列表隔开:
setAcl /hadoop ip:192.168.133.133:cdrwa,ip:192.168.133.132:cdrwa
-
create /hadoop "hadoop" # 初始化测试用的结点 addauth digest itcast:123456 # 添加认证用户 setAcl /hadoop auth:itcast:cdrwa # 设置认证用户 quit # 退出后再./zkCli.sh 进入 get /hadoop # 这个时候就没有权限了,需要再次认证 addauth digest itcast:123456 # 认证,密码错了的话 zookeeper 不会报错,但是不能认证 get /hadoop
-
这里的密码是经过
SHA1
以及BASE64
处理的密文,在shell 中可以通过以下命令计算:-
echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
-
# 计算密码 echo -n itcast:12345 | openssl dgst -binary -sha1 | openssl base64 # 获取密码,设置权限列表 setAcl /hadoop digest:itcast:qUFSHxJjItUW/93UHFXFVGlvryY=:cdrwa # 现在想要get /hadoop 需要登录了 addauth digest itcast:12345 get /hadoop
-
仅需逗号隔开
-
setAcl /hadoop ip:192.168.133.132:cdrwa,auth:hadoop:cdrwa,digest:itcast:673OfZhUE8JEFMcu0l64qI8e5ek=:cdrwa
acl 超级管理员
-
zookeeper
的权限管理模式有一种叫做super
,该模式提供一个超管,可以方便的访问任何权限的节点假设这个超管是
supper:admin
,需要为超管生产密码的密文echo -n super:admin | openssl dgst -binary -sha1 | openssl base64
-
那么打开
zookeeper
目录下/bin/zkServer.sh
服务器脚本文件,找到如下一行:/nohup # 快速查找,可以看到如下 nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}"
-
这个就算脚本中启动
zookeeper
的命令,默认只有以上两个配置项,我们需要添加一个超管的配置项"-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBvst5y6rkB6HQs="
-
修改后命令变成如下
nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBvst5y6rkB6HQs="
-
# 重起后,现在随便对任意节点添加权限限制 setAcl /hadoop ip:192.168.1.1:cdrwa # 这个ip并非本机 # 现在当前用户没有权限了 getAcl /hadoop # 登录超管 addauth digest super:admin # 强行操作节点 get /hadoop
zookeeper的 JavaAPI
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<exclusions>
<exclusion>
<artifactId>zookeeper</artifactId>
<groupId>org.apache.zookeeper</groupId>
</exclusion>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
<version>0.9</version>
</dependency>
<dependency>
<artifactId>zookeeper</artifactId>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
<groupId>org.apache.zookeeper</groupId>
<version>3.4.10</version>
</dependency>
zonde
是 zookeeper
集合的核心组件, zookeeper API
提供了一小组使用 zookeeper
集群来操作znode
的所有细节
客户端应该遵循以下步骤,与zookeeper
服务器进行清晰和干净的交互
- 连接到
zookeeper
服务器。zookeeper
服务器为客户端分配会话ID
- 定期向服务器发送心跳。否则,
zookeeper
服务器将过期会话ID
,客户端需要重新连接 - 只要会话
Id
处于活动状态,就可以获取/设置znode
- 所有任务完成后,断开与
zookeeper
服务器连接,如果客户端长时间不活动,则zookeeper
服务器将自动断开客户端
连接到Zookeeper
这部分,官网的解释十分稀少https://zookeeper.apache.org/doc/r3.4.14/zookeeperStarted.html#sc_ConnectingToZooKeeper
[zkshell: 0] help
ZooKeeper host:port cmd args
get path [watch]
ls path [watch]
set path data [version]
delquota [-n|-b] path
quit
printwatches on|off
create path data acl
stat path [watch]
listquota path
history
setAcl path acl
getAcl path
sync path
redo cmdno
addauth scheme auth
delete path [version]
deleteall path
setquota -n|-b val path
Zookeeper(String connectionString, int sessionTimeout, watcher watcher)
connectionString
-zookeeper
主机sessionTimeout
- 会话超时watcher
- 实现"监听器" 对象。zookeeper
集合通过监视器对象返回连接状态
public static void main(String[] args) throws IOException, InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
ZooKeeper zookeeper = new ZooKeeper("192.168.133.133:2181", 5000, (WatchedEvent x) -> {
if (x.getState() == Watcher.Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
});
countDownLatch.await();
System.out.println(zookeeper.getSessionId());
zookeeper.close();
}
新增节点
-
// 同步 create(String path, byte[] data, List<ACL> acl, CreateMode createMode) // 异步 create(String path, byte[] data, List<ACL> acl, CreateMode createMode, AsynCallback.StringCallback callBack, Object ctx)
-
参数 解释 path
znode
路径data
数据 acl
要创建的节点的访问控制列表。 zookeeper API
提供了一个静态接口ZooDefs.Ids
来获取一些基本的acl
列表。例如,ZooDefs.Ids.OPEN_ACL_UNSAFE
返回打开znode
的acl
列表createMode
节点的类型,这是一个枚举 callBack
异步回调接口 ctx
传递上下文参数
示例:
-
// 枚举的方式 public static void createTest1() throws Exception{ String str = "node"; String s = zookeeper.create("/node", str.getBytes(), ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println(s); }
-
//
自定义的方式 public static void createTest2() throws Exception{ ArrayList<ACL> acls = new ArrayList<>(); Id id = new Id("ip","192.168.133.133"); acls.add(new ACL(ZooDefs.Perms.ALL,id)); zookeeper.create("/create/node4","node4".getBytes(),acls,CreateMode.PERSISTENT); }
-
// auth public static void createTest3() throws Exception{ zookeeper.addAuthInfo("digest","itcast:12345".getBytes()); zookeeper.create("/node5","node5".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL,CreateMode.PERSISTENT); } // 自定义的方式 public static void createTest3() throws Exception{ // zookeeper.addAuthInfo("digest","itcast:12345".getBytes()); // zookeeper.create("/node5","node5".getBytes(), // ZooDefs.Ids.CREATOR_ALL_ACL,CreateMode.PERSISTENT); zookeeper.addAuthInfo("digest","itcast:12345".getBytes()); List<ACL> acls = new ArrayList<>(); Id id = new Id("auth","itcast"); acls.add(new ACL(ZooDefs.Perms.READ,id)); zookeeper.create("/create/node6","node6".getBytes(), acls,CreateMode.PERSISTENT); }
-
// digest public static void createTest3() throws Exception{ List<ACL> acls = new ArrayList<>(); Id id = new Id("digest","itcast:qUFSHxJjItUW/93UHFXFVGlvryY="); acls.add(new ACL(ZooDefs.Perms.READ,id)); zookeeper.create("/create/node7","node7".getBytes(), acls,CreateMode.PERSISTENT); }
-
// 异步 public static void createTest4() throws Exception{ zookeeper.create("/node12", "node12".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new AsyncCallback.StringCallback(){ 标签: sc连接器挂掉的原因