Zookeeper入门及使用

Posted 浪子阿志

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Zookeeper入门及使用相关的知识,希望对你有一定的参考价值。

文章目录


❤️Zookeeper简单介绍

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是典型的分布式一致性解决方案。Zoookeeper是通过共享存储的方式来实现分布式协调的,就像是电脑上面的共享文件夹。可以实现发布订阅、负载均衡、命名服务、集群管理、分布式锁等功能。

🤍Zookeeper系统模型

在Zookeeper中我们操作的最小数据单位是节点,给人的感觉有点像文件系统的树状结构,不只是使用上,创建节点的路径也给人一种文件路径的感觉。看一下可视化界面给出的效果图:

🔊Zookeeper的节点分为三类:
持久节点(Persistent): 持久节点就是创建后的节点时永久存在的(不删除的情况下),当然你要是删除了肯定没了,该节点加载到了磁盘中,也就是我们配置的dataDir目录中。
临时节点(Ephemeral): 临时节点指的是短暂的存在的节点,它与会话Session绑定在一起,会话结束后节点会被自动清理掉,临时节点不能创建子节点
顺序节点(Sequential): 顺序节点有持久顺序节点和临时顺序节点,在特性上来说与原来的节点一样,但是创建的名字后面会自动加上顺序数字。

🔊ZNode的属性:
使用stat 对应的节点可以查看对应节点的属性,例如:stat /test001

cZxid = 0x4  	// 节点创建的事务ID
ctime = Thu May 12 19:58:11 CST 2022 //节点创建的时间
mZxid = 0x4		//节点最后一次修改的事务ID
mtime = Thu May 12 19:58:11 CST 2022  //节点最后一次的修改时间
pZxid = 0x5		//子节点列表最后一次被修改的事务ID
cversion = 1	//子节点的版本号
dataVersion = 0		//内容的版本号
aclVersion = 0		//权限ACL的版本号
ephemeralOwner = 0x0	//如果临时节点保存SESSIONID,持久节点为0
dataLength = 4		//数据长度
numChildren = 1		//子节点数量

🔊ACL权限:
上面的属性中我们说到了个ACL权限,这里的权限指的是对节点的操作权限,一共分为5个权限C(Create,节点创建权限)、D(Delete,删除节点权限)、R(Read,读取节点的权限)、W(Write,更新节点的权限)、A(Admin,管理员权限)

对于权限的控制根据授权方式不同又分为4中模式:

权限模式说明
IP授权对象通过IP地址控制,例如ip:192.168.150.110或者ip:192.168.150.1/24授权一个IP或者一个网段
Digest通过用户名密码来控制,这里的密码是加密的,例如jack:sdfasdfwfwewef
World没有权限,所有人,例如:world:anyone
Super超级管理员模式

🔊Watcher通知:
Zookeeper具有发布订阅功能,这就要求订阅的主题发生变化时需要通知左右的订阅者并且获取新的主题信息,Zookeeper就是通过Watcher来实现的。
客户端可以创建并向服务段注册一个Watcher监听,监听在客户端是由WatchManager来管理的,当主题发生变化时会通过对应的Watcher来通知客户端。

❤️Zookeeper使用

Zookeeper的API操作咱就不说了,使用奈飞公司的Zookeeper开源客户端框架Curator来操作:

🤍Curator操作Zookeeper

🔊pom文件配置

<dependency>
   <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.14</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.12.0</version>
</dependency>

🔊创建会话

//创建一个重试策略,连接失败重试三次
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
//创建客户端连接
CuratorFramework client =CuratorFrameworkFactory.builder()
        .connectString("127.0.0.1:2181") //连接地址
        .sessionTimeoutMs(50000)  //会话超时时间
        .connectionTimeoutMs(30000)  //连接超时时间
        .retryPolicy(retryPolicy)  //重试机制
        .build();
//启动客户端
client.start();

🔊创建节点
创建节点需要一个path,这个path是可以是不存在的文件路径,框架会自动创建上父节点。

public static void createNode(CuratorFramework client) throws Exception 
    String path = "/testClient/test002";
    client.create()
            .creatingParentsIfNeeded()  //如果父节点不存在则自动创建
            .withMode(CreateMode.EPHEMERAL)  //创建临时节点
            .forPath(path); //创建的节点路径

看一下执行的结果:

🔊查询节点

public static  void getNode(CuratorFramework client) throws Exception 
   String path = "/testClient/test002";
    Stat stat = new Stat(); 
    byte[] bytes = client.getData().storingStatIn(stat).forPath(path);
    System.out.println(new String(bytes));

获取到了节点的内容:

🔊更新节点
更新是有个乐观锁控制的,分为两种情况,一种是更新不传入版本默认更新最新版本,另一种更新需要传入版本,先查询一下版本号再更新,咱看一下指定版本号修改:

 public static void updateNode(CuratorFramework client) throws Exception 
      String path = "/testClient/test002";
      Stat stat = new Stat(); 
      //更新并且获取新的版本号
      int version = client.setData().withVersion(stat.getVersion()).forPath(path).getVersion();
  

注意:Stat只能使用一次,下次再使用版本就过期了会报错
🔊删除节点
删除节点同样是可以通过指定版本删除的,咱看一下不通过版本删除的就行。

public static void deleteNode(CuratorFramework client) throws Exception 
     String path = "/testClient";
     client.delete()
             .deletingChildrenIfNeeded() //删除节点的同时递归删除子节点
             .forPath(path);
 

咱创建节点后延时两秒删除看一下效果:

🔊监听
Curator实现的监听方式有两种,最常用的是通过NodeCache节点缓存实现的监听。NodeCache监听分为三种:Node Cache用于节点自身的监听;Path Cache用于子节点的监听;Tree Cache既能监听子节点也能监听自身。
看一下简单的Node Cache实现方式:
需要引入对应的pom文件,这里面包含了Zookeeper的一些典型应用场景

<dependency>
     <groupId>org.apache.curator</groupId>
     <artifactId>curator-recipes</artifactId>
     <version>2.12.0</version>
</dependency>

看一下代码实现:

public static void nodeTreeCache(CuratorFramework client) throws Exception 
    String path = "testClient";
     //如果path存在,删除路径
     Stat stat = client.checkExists().forPath("/" + path);
     if (stat != null) 
         client.delete().guaranteed().deletingChildrenIfNeeded().forPath("/" + path);
     
     //创建path
     client.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/" + path, path.getBytes());
     //参数:true代表缓存数据到本地
     PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/" + path, true);
     //BUILD_INITIAL_CACHE 代表使用同步的方式进行缓存初始化。
     pathChildrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
     //监听事件,监听节点的变化
     pathChildrenCache.getListenable().addListener((cf, event) -> 
         PathChildrenCacheEvent.Type eventType = event.getType();
         switch (eventType) 
             case CONNECTION_RECONNECTED:
                 pathChildrenCache.rebuild();
                 break;
             case CONNECTION_SUSPENDED:
                 break;
             case CONNECTION_LOST:
                 System.out.println("链接丢失---------");
                 break;
             case CHILD_ADDED:
                 System.out.println("增加子节点------");
                 break;
             case CHILD_UPDATED:
                 System.out.println("更新子节点-------");
                 break;
             case CHILD_REMOVED:
                 System.out.println("删除子节点-------");
                 break;
             default:
         
     );
 

让这个程序先sleep1分钟,咱再起一个客户端来新增删除当前路径下的节点来看一下效果:

❤️Zookeeper应用场景

🔊数据发布订阅
发布订阅是将数据发布到Zookeeper的节点上,使用的应用通过订阅节点类动态的获取数据,实现配置数据的几种管理和动态更新。例如使用Zookeeper做配置中心,将yml中的数据库配置或其他一些外部配置发布到Zookeeper对应的节点路径下面,通过节点路径判断当前服务的配置位置,然后再获取子节点中对应的配置信息加载到代码中,实现动态配置。

🔊命名服务
命名服务是分布式项目中常见的一种场景,可以通过指定的名字来获取对应的资源或地址。Zookeeper会在自己的文件系统上创建一个以路径为名称的节点,它可以指向提供的服务的地址,远程对象等。简单来说使用Zookeeper做命名服务就是用路径作为名字,路径上的数据就是其名字指向的实体,例如常见的Zookeeper在Dobbo中的使用。

🔊分布式协调
在分布式项目中我们很多数据是通过MQ来传递操作的,我们通过MQ只能知道当前数据被操作了,但是不清楚执行的结果,到底是执行成功了还是执行失败了,这时候可以通过Zookeeper来通知操作的结果。
设置一个分布式系统中不只是两步操作,有可能是一系列的操作,就可以通过Zookeeper中的内容一直更新,来通知各个节点走到了哪一步了。

🔊分布式锁
在分布式项目中假设我们需要加锁,因为是多个JVM环境一个机器上加的锁对另一个机器上是无效的,所以可以通过zookeeper来实现。例如我们多个机器在zookeeper上创建一个节点,谁先创建出来该节点谁获取到锁,当其他机器创建的时候因为节点已经存在了,所以就报错了,当然这个错try了,然后循环一直创建,锁用完之后会删除节点,省下的机器继续看谁先创建出节点来。

Zookeeper的应用场景还有很多,都是应用其类似于共享文件的属性来设计的,例如用顺序节点来做数据库主键自增等。

❤️Zookeeper集群

🤍集群中的角色

我们以前接触到的集群都是有Master和Slave两个概念的,但是在Zookeeper中没有继续使用这两个角色。而是启用了新的角色系统,分别是:Leader、Follower、Observer三种⻆⾊。
🔊Leader
Leader是Zookeeper集群中的核心,是集群机服务的领导者,相当于Master,Leader负责集群中事务的调度和处理,保证事务执行的顺序性。
来看一下Leader是怎么工作的:

Leader的请求执行过程是由这7个请求处理器来执行的,执行的过程很简单,非事务请求会按照蓝色绿色这一顺序进行执行;如果是事务请求事务投票处理器会判断当前的请求处理是否是事务请求,如果是事务请求会到下面的黄色部分阻塞请求,同时阻塞Commit请求处理器,直到接收到过半的Follower的应答之后才继续往下执行。
注意:Zookeeper不会等待所有的Follower都应答之后执行,而是如果有过半的应答了就执行执行Commit了。
🔔Leader的选举过程
集群中的Leader是通过投票选举的,但是选举的规则是固定的,每个Looking状态的服务都会发送自身的ServerId(也就是配置的myid)和事务Id(ZXID)。类似于(1,2)这样子,首先判断的是事务ID,事务ID最大的作为Leader;如果事务ID都一样那么就判断服务ID,服务ID大的作为Leader。(后面ZAB协议的时候会介绍原因)

🔊Follower
Follower是Zookeeper集群中的跟随者,可以处理非事务请求,如果是事务请求的话转发给Leader进行处理;参与事务投票,上面说的过半投票就是由所有的Follower进行投票的;
来看一下Follower的处理流程:

FollowerRequest主要是用来判断是否是事务请求,如果是事务请求直接发送给Leader。非事务流程的话直接执行返回给客户端对应的响应;后面的事务流程指的是Leader中的事务需要发送给Follower进行应答,所以后面有个SendAckRequest应答请求处理器。

🔊Observer
Observer是Zookeeper中的观察者,它就类似于观众的角色。它的工作流程与Follower一样,同样将接收到的事务请求发送给Leader,非事务请求自己处理。但是他不会参与投票(包括事务应答、Leader选举)。

🤍ZAB协议

ZAB协议是Zookeeper请求处理的特有协议,和Paxos协议差不多,但是在Paxos协议的基础上又做了一些处理,专门为Zookeeper设计的一种协议。
ZAB协议包括两种基本模式:消息广播和崩溃恢复模式。

🔊消息广播

看一下消息广播的执行流程:首先正常一个事务请求最终都是传到Leader的,然后Leader服务会将每一个事务请求生成事务Proposal进行广播,广播的Proposal是有一个唯一标识事务ID(ZXID)的,这里的事务ID是自增的,同时会将事务Proposal放到队列中,保证了事务执行的顺序性。
Follower接收到事务Proposal之后,会将事务写到本地日志中,然后返回给Leader一个Ack响应。当超过一半的Follower返回Ack响应之后**(ZAB协议移除了中断机制)**,Leader就会广播Commit消息给所有的Follower通知其需要提交事务。Follower接收到消息后提交事务,Leader自身也会提交事务。

🔊崩溃恢复
崩溃恢复主要需要干两件事:选举新的Leader和数据同步

崩溃恢复指的是在消息广播的过程中,因为某些原因Leader失联了,国不可一日无主啊,于是我们需要重新选取一个Leader,选取Leader的规则是啥呢?首先因为事务的有序性和事务ID的自增,所以要选取事务ID最大的那个,它保存的事务最多的,也是数据最完整的一个。
选举出新的Leader之后,就需要恢复之前的运行了,通知其他机器选出来新的Leader了,并且对数据进行同步,按照新的Leader的事务记录把事务的执行先同步到一致。

以上是关于Zookeeper入门及使用的主要内容,如果未能解决你的问题,请参考以下文章

ZooKeeper核心原理及应用场景

ZooKeeper核心原理及应用场景

ZooKeeper核心原理及应用场景

Zookeeper 从入门到精通Zookeeper 下载安装及配置

基础组件1Flume入门Agent

Dubbox与Zookeeper简介及入门小案例