Zookeeper
这里目录标题
1、ZooKeeper简介
ZooKeeper是一个开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。在我们的课程中,ZooKeeper主要的角色是当做服务注册中心存在,我们将编写好的服务注册至ZooKeeper服务注册中心。
服务注册中心,给客户端提供可供调用的服务列表,客户端在进行远程服务调用时,根据服务列表然后选择服务提供方的服务地址进行服务调用。服务注册中心在分布式系统中大量应用,是分布式系统中不可或缺的组件,例如rocketmq的name server,hdfs中的namenode,dubbo中的zk注册中心,spring cloud中的服务注册中心eureka。
官方(Dubbo注册中心)推荐使用 ZooKeeper注册中心。注册中心负责服务地址的注册与查找,相当于目录服务。
Zookeeper是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为Dubbo 服务的注册中心,工业强度较高,可用于生产环境。
2、搭建ZooKeeper集群
搭建Zookeeper 3.4.5:
https://blog.csdn.net/weixin_43660536/article/details/118369963.
2.10、 ZKServer的命令
一、zk服务命令
1. 启动ZK服务: bin/zkServer.sh start
2. 查看ZK服务状态: bin/zkServer.sh status
3. 停止ZK服务: bin/zkServer.sh stop
4. 重启ZK服务: bin/zkServer.sh restart
5. 连接服务器: zkCli.sh -server 127.0.0.1:2181
二、连接zk
Linux环境下:
eg、zkCli.sh -server 127.0.0.1:2181
三、zk客户端命令
1.ls -- 查看某个目录包含的所有文件,例如:
[zk: 127.0.0.1:2181(CONNECTED) 1] ls /
ls /path
2.ls2 -- 查看某个目录包含的所有文件,与ls不同的是它查看到time、version等信息,例如:
[zk: 127.0.0.1:2181(CONNECTED) 1] ls2 /
3.create -- 创建znode,并设置初始内容,例如:
[zk: 127.0.0.1:2181(CONNECTED) 1] create /test "test"
Created /test
创建一个新的 znode节点“ test ”以及与它关联的字符串
create /path data 默认创建持久节点
create -s /path data 创建顺序节点
create -e /path data 创建临时节点
create /parent/sub/path /data
4.get -- 获取znode的数据,如下:
[zk: 127.0.0.1:2181(CONNECTED) 1] get /test
get /path
get /path0000000018 访问顺序节点必须输入完整路径
5.set -- 修改znode内容,例如:
[zk: 127.0.0.1:2181(CONNECTED) 1] set /test "ricky"
set /path /data
6.delete -- 删除znode,例如:
[zk: 127.0.0.1:2181(CONNECTED) 1] delete /test
delete /path 删除没有子节点的节点
rmr /path 移除节点并且递归移除所有子节点
7.quit -- 退出客户端
8.help -- 帮助命令
3、zookeeper存储模型
3.1. 存储结构
- zookeeper是一个树状结构,维护一个小型的数据节点znode 。
- 数据以key,value的方式存在,目录是数据的key 。
- 所有的数据访问都必须以绝对路径的方式呈现。
[zk: localhost:2181(CONNECTED) 10] get /yjx
666 当前节点的值
cZxid = 0xf00000013
创建这个节点的事务id,ZXID是一个长度64位的数字,
低32位是按照数字递增,即每次客户端发起一个proposal,低32位的数字简单加1。
高32位是leader周期的epoch编号
ctime = Mon Dec 09 17:33:06 CST 2019 创建时间
mZxid = 0xf00000013 最后一次修改节点数据的事务ID
mtime = Mon Dec 09 17:33:06 CST 2019 修改时间
pZxid = 0xf00000014 子节点的最新事务ID
cversion = 1 对此znode的子节点进行的更改次数
dataVersion = 对此znode的数据所作的修改次数
aclVersion = 对此znode的acl更改次数
ephemeralOwner = 0x0 (持久化节点)0x16ee9fc0feb0001(临时节点)
dataLength = 3 数据的长度
numChildren = 1 子节点的数目
3.2. 节点的分类
- 持久化节点(PERSISTENT)
- 默认创建的就是持久化节点
- 临时节点(Ephemral)
- 只要创建节点的会话有效,节点就不会失效 ;
- 可以被所有的客户端所查看 ;
- 事务编号和临时节点编号是一致的;
- create -e
- 一旦会话结束,临时节点也会被自动删除,一般这个功能用于判断节点和服务器是否保持连接 ;
- 序列化节点(Sequential)
- 在名字的后面添加一个序列号(有序)
- create -s
4、zkServer的监听机制
-
一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端,以便通知它们。
-
机制特点
- 一次性触发;一个watcher event会被发送到client。只有一次;
- watcher event异步发送
-
数据监视
-
Zookeeper有数据监视和子数据监视 getdata() and exists() 设置数据监视,getchildren()设 置了子节点监视
//watch监听有不同的类型,有监听状态的stat ,内容的get,目录结构的ls。 get /path [watch] NodeDataChanged stat /path [watch] NodeDeleted ls /path [watch] NodeChildrenChanged
-
父节点Watcher事件类型:
创建父节点触发: NodeCreated
修改父节点数据触发: NodeDataChanged
删除父节点触发: NodeDeleted
-
子节点Watcher事件类型:
使用ls命令为父节点设置watcher,子节点被创建时触发:NodeChildrenChanged
使用ls命令为父节点设置watcher,子节点被删除时触发:NodeChildrenChanged
使用ls命令为父节点设置watcher,子节点被修改时,不触发事件
-
5、ZAB协议
- ZAB(Zookeeper Atomic Broadcast) 协议是为分布式协调服务zookeeper专门设计的一种支持崩溃恢复的原子广播协议。
- ZAB是ZooKeeper实现分布式数据一致性的核心算法,ZAB借鉴Paxos算法
- 在zookeeper中,主要依赖ZAB协议来实现分布式数据一致性,
- 基于该协议,zookeeper实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。
5.2. ZAB协议阶段
- ZAB协议的三个阶段【发现,同步,广播】
- 发现:即要求zookeeper集群必须选择出一个leader进程,同时leader会维护一个follower可 用列表。将来客户端可以这follower中的节点进行通信。
- 同步:leader要负责将本身的数据与follower完成同步,做到多副本存储。这样也是体现了 CAP中CP。follower将队列中未处理完的请求消费完成后,写入本地事物日志中。
- 广播:leader可以接受客户端新的proposal请求,将新的proposal请求广播给所有的 follower。
5.3. 协议核心
- 协议核心
- 定义了对于那些会改变ZooKeeper服务器数据状态的事务请求处理方式;
- 所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为Leader服务 器,而余下的其他服务器称为Follower服务器。Leader服务器负责将一个客户端事务请求转 换成一个事务Proposal(提议),并将该Proposal分发给集群中所有的Follower服务器。之后 Leader服务器需要等待所有的Follower服务器的反馈,一旦超过半数的Follower服务器进行 了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将 前一个Proposal进提交。
5.4. ZAB协议模式
- ZAB协议包含两种基本模式,分别是:
- 1》崩溃恢复之数据恢复
- 当整个集群正在启动时,或者当leader节点出现网络中断、崩溃等情况时,ZAB协议就会进入恢复模式并选举产生新的leader,当leader服务器选举出来后,并且集群中有过半的机器和该leader节点完成数据同步后(同步指的是数据同步,用来保证集群中过半的机器能够和leader服务器的数据状态保持一致),ZAB协议就会退出恢复模式。
- 2》消息广播之原子广播
- 当集群中已经有过半的Follower节点完成了和Leader状态同步以后,那么整个集群就进入了消息广播模式。这个时候,在Leader节点正常工作时,启动一台新的服务器加入到集群,那这个服务器会直接进入数据恢复模式,和leader节点进行数据同步。同步完成 后即可正常对外提供非事务请求的处理。
6、ACL权限控制(了解)
ACL权限控制
ZK的节点有5种操作权限:CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,
这5种权限简写为crwda,这5种权限中,delete是指对子节点的删除权限,其它4种权限指对自身节点的操作权限
身份的认证有4种方式:
- world:默认方式,相当于全世界都能访问
- auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授
权用户)
- digest:即用户名:密码这种方式认证,这也是业务系统中最常用的
- ip:使用Ip地址认证
schema:
- world:只有一个用户anyone,代表所有人(默认)
- ip:使用IP地址认证
- auth:使用已添加认证的用户认证
- digest:使用用户名:密码 方式认证
id:
- world:只有一个id,anyone
- ip:通常是一个ip地址或者地址段
- auth:用户名
- digest:自定义
权限:
- create 简写为c,可以创建子节点
- delete 简写为d 可以删除子节点
- read 简写为r 可以读取节点数据及显示子节点列表
- write 简写为w 可以设置节点数据
- admin 简写为a 可以设置管理权限
查看ACL:
- getAcl /parent
设置ACL:
- setAcl /parent world:anyone:r
添加用户:
- addauth digest zhangsan:123456
- addauth digest lisi:123456
设置权限:
- setAcl /parent auth:zhangsan:123456:r
- setAcl /parent auth:lisi:123456:rcwd
7、分布式协调框架
- 场景一:统一命名服务
- 有一组服务器向客户端提供某种服务,我们希望客户端每次请求服务端都可以找到服务端集群 中某一台服务器,这样服务端就可以向客户端提供客户端所需的服务。对于这种场景,我们的 程序中一定有一份这组服务器的列表,每次客户端请求时候,都是从这份列表里读取这份服务 器列表。那么这分列表显然不能存储在一台单节点的服务器上,否则这个节点挂掉了,整个集 群都会发生故障,我们希望这份列表时高可用的。高可用的解决方案是:这份列表是分布式存 储的,它是由存储这份列表的服务器共同管理的,如果存储列表里的某台服务器坏掉了,其他 服务器马上可以替代坏掉的服务器,并且可以把坏掉的服务器从列表里删除掉,让故障服务器 退出整个集群的运行,而这一切的操作又不会由故障的服务器来操作,而是集群里正常的服务 器来完成。这是一种主动的分布式数据结构,能够在外部情况发生变化时候主动修改数据项状 态的数据机构。,它和javaEE里的JNDI服务很像。
- 场景二:分布式锁服务。
- 当分布式系统操作数据,例如:读取数据、分析数据、最后修改数据。在分布式系统里这些操 作可能会分散到集群里不同的节点上,那么这时候就存在数据操作过程中一致性的问题,如果 不一致,我们将会得到一个错误的运算结果,在单一进程的程序里,一致性的问题很好解决, 但是到了分布式系统就比较困难,因为分布式系统里不同服务器的运算都是在独立的进程里, 运算的中间结果和过程还要通过网络进行传递,那么想做到数据操作一致性要困难的多。 Zookeeper提供了一个锁服务解决了这样的问题,能让我们在做分布式数据运算时候,保证 数据操作的一致性。
- 场景三:配置管理。
- 在分布式系统里,我们会把一个服务应用分别部署到n台服务器上,这些服务器的配置文件是 相同的(例如:我设计的分布式网站框架里,服务端就有4台服务器,4台服务器上的程序都 是一样,配置文件都是一样),如果配置文件的配置选项发生变化,那么我们就得一个个去改 这些配置文件,如果我们需要改的服务器比较少,这些操作还不是太麻烦,如果我们分布式的 服务器特别多,比如某些大型互联网公司的hadoop集群有数千台服务器,那么更改配置选项 就是一件麻烦而且危险的事情。这时候zookeeper就可以派上用场了,我们可以把zookeeper 当成一个高可用的配置存储器,把这样的事情交给zookeeper进行管理,我们将集群的配置文 件拷贝到zookeeper的文件系统的某个节点上,然后用zookeeper监控所有分布式系统里配置 文件的状态,一旦发现有配置文件发生了变化,每台服务器都会收到zookeeper的通知,让每 台服务器同步zookeeper里的配置文件,zookeeper服务也会保证同步操作原子性,确保每个 服务器的配置文件都能被正确的更新。
- 场景四:为分布式系统提供故障修复的功能。
- 集群管理是很困难的,在分布式系统里加入了zookeeper服务,能让我们很容易的对集群进行 管理。集群管理最麻烦的事情就是节点故障管理,zookeeper可以让集群选出一个健康的节点 作为master,master节点会知道当前集群的每台服务器的运行状况,一旦某个节点发生故 障,master会把这个情况通知给集群其他服务器,从而重新分配不同节点的计算任务。 Zookeeper不仅可以发现故障,也会对有故障的服务器进行甄别,看故障服务器是什么样的 故障,如果该故障可以修复,zookeeper可以自动修复或者告诉系统管理员错误的原因让管理 员迅速定位问题,修复节点的故障。大家也许还会有个疑问,master故障了,那怎么办了? zookeeper也考虑到了这点,zookeeper内部有一个“选举领导者的算法”,master可以动态选 择,当master故障时候,zookeeper能马上选出新的master对集群进行管理。
8、java访问zookeeper
-
pom.xml 信息
<dependencies> <!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.5</version> </dependency> </dependencies>
-
封装工具类
package com.xxxx; import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; import java.util.List; import java.util.concurrent.CountDownLatch; public class BaseZookeeper implements Watcher { private ZooKeeper zookeeper; //超时时间 private static final int SESSION_TIME_OUT = 2000; //countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行 private CountDownLatch countDownLatch = new CountDownLatch(1); public void process(WatchedEvent event) { if (event.getState() == Event.KeeperState.SyncConnected) { System.out.println("Watch received event[" + event + "]"); countDownLatch.countDown(); } } /** * 连接zookeeper * * @param connectString * @throws Exception */ public void connectZookeeper(String connectString) throws Exception{ zookeeper = new ZooKeeper(connectString, SESSION_TIME_OUT,this); countDownLatch.await(); System.out.println("zookeeper connection success"); } /** * 创建节点 * * @param path * @param data * @throws Exception */ public String createNode(String path, String data) throws Exception { return this.zookeeper.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } /** * 获取路径下所有子节点 * * @param path * @return * @throws KeeperException * @throws InterruptedException */ public List<String> getChildren(String path) throws KeeperException,InterruptedException { List<String> children = zookeeper.getChildren(path, false); return children; } /** * 获取节点的数据 * * @param path 路径 * @return * @throws KeeperException * @throws InterruptedException */ public String getData(String path) throws KeeperException,InterruptedException { byte[] data = zookeeper.getData(path, false, null); if (data == null) { return ""; } return new String(data); } /** * 设置节点数据 * * @param path 路径 * @param data 数据 * @return * @throws KeeperException * @throws InterruptedException */ public Stat setData(String path, String data) throws KeeperException, InterruptedException { Stat stat = zookeeper.setData(path, data.getBytes(), -1); return stat; } /** * 删除节点 * * @param path * @throws InterruptedException * @throws KeeperException */ public void deleteNode(String path) throws InterruptedException,KeeperException { zookeeper.delete(path, -1); } /** * 获取创建时间 * * @param path * @return * @throws KeeperException * @throws InterruptedException */ public String getCTime(String path) throws KeeperException,InterruptedException { Stat stat = zookeeper.exists(path, false); return String.valueOf(stat.getCtime()); } /** * 获取某个路径下孩子的数量 * * @param path * @return * @throws KeeperException * @throws InterruptedException */ public Integer getChildrenNum(String path) throws KeeperException,InterruptedException { int childenNum = zookeeper.getChildren(path, false).size(); return childenNum; } /** * 关闭连接 * * @throws InterruptedException */ public void closeConnection() throws InterruptedException { if (zookeeper != null) { zookeeper.close(); } } }
-
测试类
package com.xxxx; public class ZookeeperDemo { public static void main(String[] args) throws Exception { //创建连接 BaseZookeeper baseZookeeper = new BaseZookeeper(); //创建客户端 baseZookeeper.connectZookeeper("node01:2181,node02:2181,node03:2181 "); //创建节点 // baseZookeeper.createNode("/yjx/node" + Math.random(), "666"); //删除节点 // baseZookeeper.deleteNode("/yjx/bd"); //关闭连接 baseZookeeper.closeConnection(); } }
9、四字命令(了解)
9.1. 安装nc
- yum install nc -y
- 如果出现错误,请重新清空和重构cache
9.2. 四字命令
- ZooKeeper 支持某些特定的四字命令(The Four Letter Words)与其进行交互。
- 使用方式,在shell终端输入:
echo 四字命令| nc node01 2181