Zookeeper学习笔记

Zookeeper 学习笔记

Zookeeper工作模式

zookeeper工作模式

1.基本概念:

① 集群角色

​ 集群典型的模式是Master/Slave模式,通常可以操作写入的称为Master,提供读服务的机器称为Slave。但是在Zookeeper中,颠覆了这种模式,引入了MasterFollowerObserver三种角色

  • Master

所有机器通过Leader选举算法,决定一台为Leader,Leader提供读写服务

  • Follower
  • Observer

​ Observer 和 Follower都提供读服务,区别在于Observer不参与Leader选举

② 会话(Session)

Session指客户端会话,一个客户端连接是指客户端和服务端之间的一个TCP长连接,Zookeeper对外服务端默认端口是2181,客户端启动会与服务端发送一个TCP连接,通过该连接客户端发送心跳监测保持服务端会话,也能向Zookeeper发送请求响应请求,同时还能通过该连接接收服务器Watch事件通知。

③ 数据节点

我们通常将组成集群的机器当做一个“节点”,但在Zookeeper中“节点”分为两种

  • 机器节点(构成集群的机器)

  • 数据节点(数据模型中的数据单元[znode])

Zookeeper 将数据存储在内存中,数据模型是树,类似系统的目录结构,由"/"进行分割的路径就是一个Node,列入/app/path1,每个Node都会存储自己的数据内容和属性信息。

④ 版本

Zookeeper每个Znode都会存储数据,每个ZNode都会维护一个Stat的数据结构来记录ZNode的三个数据版本

  • version【ZNode的版本】
  • cversion【Znode子节点的版本】
  • aversion【Znode的ACL版本】

⑤ Watcher(事件监听器)

Zookeeper允许客户端在指定节点注册Watcher监听,当特定时间触发后,服务端会发送时间通知到监听的客户端。

⑥ ACL

ACL是Zookeeper用来进行权限控制的一种策略模式,定义了五种权限:

  • CREATE:创建子节点权限
  • READ:获取节点数据和子节点列表权限
  • WRITE:更新节点数据权限
  • DELETE:删除节点权限
  • ADMIN:设置节点ACL权限

2.Zookeeper环境搭建

1.单机模式

1. 解压

tar -zxvf zookeeper-3.4.14.tar.gz

2. 进入 zookeeper-3.4.14目录创建data文件夹

cd zookeeper-3.4.14
mkdir data

3. 修改配置文件名称

cd conf
mv zoo_sample.cfg zoo.cfg

4. 修改zoo.cfg中data属性

dataDir = /root/zookeeper-3.4.14

5. Zookeeper启动

cd /root/zookeeper-3.4.14/bin
#启动服务
./zkServer.sh start
#关闭服务
./zkServer.sh stop
#查看状态
./zkServer.sh status

2.伪集群模式

clientPort端口

伪集群模式下,每个服务的端口不能重复,通过修改clientPort

dataDir和dataLogDir

dataDirdataLogDir目录也要区分,用于存放数据文件和日志文件。

server.X和myid:

server.X 的X对应data/myId的数字,服务的myid分别写入1.2.3,那么server的zoo.cfg都配置成server.1 ,server.2,server.3。

创建⽬录zkcluste

mkdir zkcluster

解压 zookeeper-3.4.14.tar.gz到zkcluster⽬录下

tar -zxvf zookeeper-3.4.14.tar.gz -C /zkcluster

改变名称

mv zookeeper-3.4.14 zookeeper01

复制其他集群服务并改名

cp -r zookeeper01/ zookeeper02
cp -r zookeeper01/ zookeeper03

分别在zookeeper01、zookeeper02、zookeeper03⽬录下创建data和logs目录

mkdir data
cd data
mkdir logs

修改zoo.cfg配置信息

clientPort=2181
dataDir=/zkCluster/zookeeper01/data
dataLogDir=/zkCluster/zookeeper01/data/logs

clientPort=2182
dataDir=/zkCluster/zookeeper02/data
dataLogDir=/zkCluster/zookeeper02/data/logs

clientPort=2183
dataDir=/zkCluster/zookeeper03/data
dataLogDir=/zkCluster/zookeeper03/data/logs

配置集群
1)在每个zookeeper的data目录下创建一个myid文件,内容设置服务器的id,自定义设置内容,例如:(1,2,3)

vim myid 

2)每个zookeeper的zoo.cfg文件中配置客户端访问端口(clientPort)和集群服务列表

server.1=10.13.52.50:2881:3881
server.2=10.13.52.50:2882:3882
server.3=10.13.52.50:2883:3883
#server.服务器ID=服务器IP地址:服务器之间通讯端口:服务器之间投票选举端口

启动集群

3.Zookeeper基本使用

Zookeeper系统模型

Zookeeper数据信息保存在一个个数据节点上,这些节点称为Znode是Zookeeper中最小的单位,ZNode下可以再挂ZNode,形成一个树形结构。

image-20210307222839509

ZNode的类型

Zookeeper的节点类型分为三大类

  • 持久性节点(Persistent)
  • 临时性节点(Ephemeral)
  • 顺序性节点(Sequential)

开发创建节点的时候通过组合可以创建四种节点:

  • 持久节点:Zookeeper常见的节点类型,创建后就一直存在,除非主动删除。
  • 持久顺序节点:相对持久节点,具有额外的顺序特性,在节点创建的时候会在节点名后面加上一个数字后缀用于表示顺序
  • 临时节点:当客户端会话结束,节点会被删除,相对持久性节点,临时节点不能创建子节点。
  • 临时顺序节点:和持久顺序类似,但是会随着会话关闭删除节点

事务ID

在zookeeper的事务是指能改变zookeeper服务器状态的操作,也称之为事务操作或者更新操作,一般包括数据节点创建删除,节点内容更新操作,每个请求都会分配一个全局唯一的ID,用ZXID表示,通常是一个64位的数字,每个ZXID对应一次更新操作,通过ZID可以间接的识别出Zookeeper处理更新操作的全局顺序.

ZNode的状态信息查看

ls2 /zookeeper

Watcher-数据变更通知

Zookeeper使用Watcher机制实现分布式数据的发布订阅功能。

Zookeeper允许客户端向服务端注册一个Watcher监听,当服务器的一些指定事件触发了Watcher,那么会向指定客户端发送一个事件通知,实现分布式的通知功能。

整个Watcher注册与通知过程如图所示。

image-20210307223607389

Zookeeper的Watcher机制主要包括客户端线程、客户端WatcherManager、Zookeeper服务器三部分。

具体工作流程为:

① 客户端向Zookeeper服务器注册并同时将Watcher对象存储在客户端的WatcherManager中。

② Zookeeper触发Watcher事件后,向客户端发送通知。

③ 客户端线程从WatcherManager中取出对应Watcher对象执行回调逻辑。

ACL模型-保障数据安全

权限模式(Scheme)授权对象(ID)权限(Permission):【scheme​ :id :permisson】表示一个有效的ACL信息。

Zookeeper命令行操作

连接客户端
./bin/zkCli.sh #连接本地的zookeeper服务器
./bin/zkCli.sh -server 127.0.0.1:2182 #连接指定的服务器
创建节点
create [-s] [-e] path data acl
-s #表示创建顺序节点 
-e #表示创建临时节点
#如不指定,默认创建持久性节点 ; 
#acl 设置权限控制

创建顺序节点

create -s /zk-test 123

创建临时节点

create -e /zk-temp 123

临时节点当客户端连接退出后会自动销毁。

创建持久节点

create /zk-permanent 123
读取节点
ls path #查看指定节点下一级的所有节点
get path #查看指定节点的数据内容和属性信息
ls2 path #查看指定节点的子节点列表和属性信息
更新节点
set path data [version]
path #表示当前更新节点指定路径
data #表示更新内容
version #表示数据版本

set /zk-permanent 456
#将指定节点数据更新为456
删除节点

使用delete命令删除zookeeper下的指定节点

delete path [version]

如有子节点的话,不能删除

官方API使用

<dependency>
	<groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
	<version>3.4.14</version>
</dependency>

开源客户端

ZkClient

添加依赖

<dependency>
	<groupId>com.101tec</groupId>
	<artifactId>zkclient</artifactId>
	<version>0.2</version>
</dependency>

操作demo

package com.hust.grid.leesf.zkclient.examples;
import java.io.IOException;
import org.I0Itec.zkclient.ZkClient;
public class CreateSession {
	/*
		创建⼀个zkClient实例来进⾏连接
		注意:zkClient通过对zookeeperAPI内部包装,将这个异步的会话创建过程同步化了
	*/
	public static void main(String[] args) {
        //创建连接
        ZkClient zkClient = new ZkClient("127.0.0.1:2181");
        System.out.println("ZooKeeper session established.");
        
        //创建节点
        //createParents的值设置为true,可以递归创建节点
		zkClient.createPersistent("/lg-zkClient/lg-c1",true);
		System.out.println("success create znode.");
        
        //删除节点
        String path = "/lg-zkClient/lg-c1";
        zkClient.deleteRecursive(path);
        
        //获取子节点
        List<String> children = zkClient.getChildren("/lg-zkClient");
        
        //注册监听事件
         zkClient.subscribeChildChanges(path, new IZkChildListener() {
            //节点改变事件
			public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
				System.out.println(parentPath + " 's child changed,
				currentChilds:" + currentChilds);
			}
			//节点删除事件
			public void handleDataDeleted(String s) throws Exception {
				System.out.println(s+" 该节点被删除");
			}                      
		});
                
		//判断节点是否存在
		boolean exists = zkClient.exists(path);
                
		//获取节点内容
		Object o = zkClient.readData(path);
 		//更新
		zkClient.writeData(path,"4567");		
		//删除
        zkClient.delete(path);       
    }
}
Curator

添加依赖

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

操作demo

package com.hust.grid.leesf.curator.examples;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class Create_Session_Sample {
	public static void main(String[] args) throws Exception {
		RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
		//方式一、new client 方式创建连接
        CuratorFramework client = CuratorFrameworkFactory.newClient(
			"127.0.0.1:2181"
            , 5000
            , 3000
            ,retryPolicy
        );
		client.start();
		System.out.println("Zookeeper session1 established. ");
        //方式二、builder链式创建连接
		CuratorFramework client1 = CuratorFrameworkFactory.builder()
			.connectString("127.0.0.1:2181") //server地址
			.sessionTimeoutMs(5000) // 会话超时时间
			.connectionTimeoutMs(3000) // 连接超时时间
			.retryPolicy(retryPolicy) // 重试策略
			.namespace("base") // ⽴命名空间/base
			.build(); //
		client1.start();
		System.out.println("Zookeeper session2 established. ");

创建节点

  • 创建一个内容为空的节点
client.create().forPath(path);

默认创建持久节点,内容为空。

  • 创建一个包含内容的节点
client.create().forPath(path,"我是内容".getBytes());

Curator内容使用byte[]作为方法参数

  • 递归创建父节点,并指定创建的节点类型
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path);

creatingParentsIfNeeded如果父节点不存在则创建

代码实例

package com.hust.grid.leesf.curator.examples;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
	public static void main(String[] args) throws Exception {
		CuratorFramework client = CuratorFrameworkFactory.builder()
			.connectString("127.0.0.1:2181") //server地址
			.sessionTimeoutMs(5000) // 会话超时时间
			.connectionTimeoutMs(3000) // 连接超时时间
			.retryPolicy(new ExponentialBackoffRetry(1000,5)) //重试策略
		.build(); //
		client.start();
		System.out.println("Zookeeper session established. ");
		//添加节点
		String path = "/lg-curator/c1";
		client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path,"init".getBytes());
		System.out.println("success create znode"+path);
	}
}

删除节点

  • 删除一个子节点
client.delete().forpath(path);
  • 删除节点并递归删除其子节点
client.delete().deletingChildrenIfNeeded().forPath(path);
  • 删除指定版本数据
client.delete().withVersion(1).forPath(path);

如果版本不存在,删除异常
org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode = BadVersion for …

  • 强制保证删除一个节点
client.delete().guaranteed().forPath(path);

采用guaranteed强制删除,只要客户端会话有效,Curator会在后台持续删除动作,直到节点删除成功为止,在网络不稳定情况下,很有效果。

演示实例:

package com.hust.grid.leesf.curator.examples;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
	public static void main(String[] args) throws Exception {
		CuratorFramework client = CuratorFrameworkFactory.builder()
			.connectString("127.0.0.1:2181") //server地址
			.sessionTimeoutMs(5000) // 会话超时时间
			.connectionTimeoutMs(3000) // 连接超时时间
			.retryPolicy(new ExponentialBackoffRetry(1000,5)) //重试策略
		.build(); //
		client.start();
		System.out.println("Zookeeper session established. ");
		//删除节点
		String path = "/lg-curator";
		client.delete().deletingChildrenIfNeeded().withVersion(-1).forPath(path);
		System.out.println("success create znode"+path);
	}
}

获取数据

//普通查询
client.getData().forPath(path);
//包含状态查询
Stat stat = new Stat();
client.getData().gstoringStatIn(stat).forPath(path);

演示实例

package com.hust.grid.leesf.curator.examples;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
public class Get_Node_Sample {
	public static void main(String[] args) throws Exception {
		CuratorFramework client = CuratorFrameworkFactory.builder()
			.connectString("127.0.0.1:2181") //server地址
			.sessionTimeoutMs(5000) // 会话超时时间
			.connectionTimeoutMs(3000) // 连接超时时间
			.retryPolicy(new ExponentialBackoffRetry(1000,5)) //重试策略
		.build(); //
		client.start();
		System.out.println("Zookeeper session established. ");
		//添加节点
		String path = "/lg-curator/c1";
		client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path,"init".getBytes());
		System.out.println("success create znode"+path);
 
		//获取节点数据
		Stat stat = new Stat();
		byte[] bytes = client.getData().storingStatIn(stat).forPath(path);
		System.out.println(new String(bytes));
 }
 }

更新数据

更新数据,如未指定版本,则系统自动更新最新版本,如果传入version则更新指定verison,如果verison已变更,则会抛出异常

//普通更新
client.setData().forPath(path,"新内容".getBytes());
//指定版本升级
client.setData().withVersion(1).forPath(path);

版本不一致异常信息:

org.apache.zookeeper.KeeperException$BadVersionException: KeeperErrorCode BadVersion for …
演示实例

package com.hust.grid.leesf.curator.examples;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
public class Set_Node_Sample {
	public static void main(String[] args) throws Exception {
		CuratorFramework client = CuratorFrameworkFactory.builder()
			.connectString("127.0.0.1:2181") //server地址
			.sessionTimeoutMs(5000) // 会话超时时间
			.connectionTimeoutMs(3000) // 连接超时时间
			.retryPolicy(new ExponentialBackoffRetry(1000,5)) //重试策略
		.build(); //
		
		client.start();
		System.out.println("Zookeeper session established. ");
		String path = "/lg-curator/c1";
		//获取节点数据
		Stat stat = new Stat();
		byte[] bytes = client.getData().storingStatIn(stat).forPath(path);
		System.out.println(new String(bytes));
		//更新节点数据
		int version = client.setData().withVersion(stat.getVersion()).forPath(path).getVersion();
		System.out.println("Success set node for : " + path + ", new version: "+version);
		client.setData().withVersion(stat.getVersion()).forPath(path).getVersion();
	}
}

5.Zookeeper应用场景

数据发布/订阅

​ 发布者将数据发布到zookeeper的一个或一系列节点上,提供订阅这进行数据订阅,打到动态获取数据的目的。实现配置信息集中式管理和数据动态更新。

​ 发布/订阅系统一般分为**推(Push)模式和拉(Pull)**模式

  • 推模式下:
    • 服务端注定将数据更新发送给所有订阅的客户端;
  • 拉模式下:
    • 客户端主动发起请求来获取最新数据,通常客户端都采用定时轮训拉取的方式。

​ Zookeeper是采用的推拉相结合的方式,客户端向服务器端注册监听节点,一旦节点数据发生变更,服务端会向对应的客户端发送Watcher事件通知,客户端收到消息通知后,需要主动到服务器获取最新的数据。

​ 通过Zookeeper的该机制,可以实现简单的配置中心,当应用启动后主动向Zookeeper服务器获取存储配置信息的节点获取数据,并注册一个Watcher监听,但凡配置信息发生改变,服务器都会通知订阅客户端,达到实时获取最新配置信息的目的。

集群管理

​ 开发运维中,会常遇到如下需求:

  • 如何快速的统计出当前生产环境下一共有多少台机器
  • 如何快速的获取到机器上下线情况
  • 如何实时监控集群中每台主机的运行时状态

传统的分布式集群管理体系是基于Agent模式,每台机器上都部署一个Agent,由Agent负责主动向指定的一个监控中心汇报自己所在机器的状态。在集群规模变大之后,存在如下弊端:

  1. 大规模升级困难
  2. 统一的Agent无法满足多样的需求
  3. 编程语言多样性

Zookeeper的两大特性

​ 1.当客户端注册Watcher监听的节点内容或者子节点列表发生变更,Zookeeper服务器就会向订阅的客户端发送变更通知。

​ 2.一旦客户端与服务器之间的会话失效,在Zookeeper上创建的临时节点也会自动删除。

​ 利用以上两大特性,可以实现集群机器存活监控,监控系统在/clusterServers节点上注册一个Watcher监听,当动态添加机器后在**/clusterServers下创建一个临时节点:/clusterServers/[Hostname]**,通过统计子节点可以试试监控机器的存活状态。

分布式日志收集系统

​ 分布式日志手机系统的核心工作就是收集分布在不同机器上的系统日志。在一个日志系统架构设计中,整个日志系统会把所有需要收集的日志机器(日志源机器)分为多个组别,每个组别对应一个收集器,对应一个后台机器(收集器机器)用于收集日志。

​ 大规模分布式日志系统,存在如下两个问题

  • 变化的日志源机器

    在生产环境中,每个机器都会伴随着(机器硬件、扩容、机房迁移或者网络问题)导致应用机器的变化。
    
  • 变化的收集器机器

    日志手机系统自身也会有机器的变更或扩容问题,会出现新收集器加入或是老收集器退出的情况。
    

Master选举

当有如下需求:多集群下,需要选取一个服务作为Master,可以通过两个方案来实现:

  • 向关系型数据库插入相同主键
多个服务同时向数据库插入相同ID,保证只能有一个客户端成功创建主键。但是关系型数据库无法通知我们Master是否挂了,通过Zookeeper可以做到这一点。
  • 利用Zookeeper的强一致性,保证只能全局唯一的节点。
多个服务同时向Zookeeper创建同一个节点,保证只能有一个客户端能创建成功。
客户端集群每天定时向Zookeeper创建一个临时节点,例如:/master/2020-11-11/binding,只有一个客户端能成功创建该节点,那么这个客户端就是Master,其他客户端针对/master/2020-11-11 注册一个子节点变更的Watcher用于监控Master是否存活,一旦Master挂了,子节点会消失,会立即通知其他客户端重新进行选举。

分布式锁

​ 分布式锁是控制分布式系统之间同时访问共享资源的一种方式。通过互斥手段防止彼此之间的干扰,保证一致性。

排它锁

​ 排它锁又称为写锁或者独占锁,是一种基本的锁类型。

①定义锁

​ 通常Java开发中,通常使用synchronized和ReentrantLock来定义锁,但是在Zookeeper中没有类似的API使用,而是通过Zookeeper的数据节点来表示锁,例如:/exclusiveLock/lock/ 节点表示为一个锁。

②获取锁

​ 在获取排它锁时,所有客户端通过create()接口,在/exclusiveLock节点下创建临时子节点/exclusiveLock/lock。利用Zookeeper的特性,最终只有一个客户端能创建成功,就可以认为创建成功的客户端获取到了锁,其他没有获取到所得客户端需要在/exclusiveLock节点上注册一个Watcher监听,用于监听lock节点的变更情况,重新竞争锁。

③释放锁

​ Zookeeper的锁释放的情况如下两种:

  1. 获得锁的客户端发生宕机,该临时节点会被移除。
  2. 业务处理完毕后,客户端主动删除创建的临时节点。
共享锁

​ 共享锁称为读锁,如果事务T1对数据对象01加上了共享锁,那么T1只能对O1进行读取操作,其他事务也只能对这个数据对象加共享锁,一直到该数据对象的所有共享锁都释放。

共享锁和其他锁得区别在于,加上排它锁后,数据对象只对一个事务可见,而加上共享锁喉,数据对所有事务都可见

通过Zookeeper实现共享锁:

①定义锁

​ 不同排它锁,通过Zookeeper定义共享锁是创建一个类似于“/sharedLock/[hostname]-请求类型-序号”的临时顺序节点。例如**/sharedLock/host1-R-0000000001**。

②获取锁

​ 在需要获取锁时,所有客户端向/sharedLock节点下创建一个临时顺序节点。

​ 读请求创建例如:/sharedLock/host1-R-0000000001的节点。

​ 写请求创建例如:/sharedLock/host1-W-0000000002的节点。

判断读写顺序

	1.创建完节点后,客户端获取/sharedLock节点下所有子节点,并对该节点变更注册监听。
	2.确定自己的节点序号在所有子节点中的顺序。
	3.对于读请求:若没有比自己小的节点或者比自己小的节点都是读请求,那么表明自己已经成功获取到了共享锁,同时开始执行读取逻辑,若有写请求,则需要等待。对于写请求:若自己不是序号最小的子节点,则需要等待。
	4.接收到Watcher通知后,重复步骤1。

③释放锁

​ 释放锁的流程与独占锁一致。

羊群效应

​ 由于每个操作完成后,会释放节点,会Watcher会通知所有监听/sharedLock子节点变更的客户端,在集群机器多的情况下会造成巨大的性能影响和网络开销。

可以有如下方案来避免羊群效应:

1.  客户端调用create接口创建/sharedLock/[hostname]-请求类型-序号的临时节点。
2. 客户端多用getChildren接口获取所有创建的子节点列表(不注册任何Watcher)
3. 当不能获取到共享锁的时候,通过exists接口对比自己晓得节点注册Watcher,对于读请求:向比自己序号小的最后一个写请求节点注册Watcher监听。 对于写请求:向比自己小的最后一个节点注册Watcher监听。
4. 等待Watcher通知,重复步骤2。

此方案优势在于,只用关注序号比自己晓得节点是否存在即可。减少性能开销。

分布式队列

​ 分布式队列可以简单分为两大类:

  • 常规的先入先出队列FIFO模型
  • 等待队列元素聚集后统一安排处理置信的Barrier模型
①FIFO先入先出

​ 使用Zookeeper实现FIFO队列,和共享锁实现类似。

1. 通过调用getChildren接口来获取/getQueue节点下的所有子节点。
2. 确定自己的节点序号在所有节点的顺序。
3. 如果不是最小序号的节点,对比自己小的节点注册Watcher监听。
4. 接收到Watcher通知后,重复步骤1。
②Barrier:分布式屏障

​ 在分布式系统中,Barrier特指系统之间的一个协调条件,规定一个队列的元素必须到达指定数量后才能统一进行处理,否则一直等待。大致设计思路如下:

​ 对/queue_barrier节点设置一个数字例如10,表示该节点下的子节点数量到达10个之后才会开启Barrier。

创建完子节点后,按照如下操作步骤执行:

1. 通过getData接口获取/queue_barrier下的数据内容:10
2. 通过调用getChildren获取/queue_barrier下的子节点数量。同时注册对/queue_barrier下子节点的Watcher监听。
3. 统计子节点数量,如果子节点数量不超过10个,继续等待。
4. 如果/queue_barrier下子节点发生变更,会发起Watcher通知,重复步骤2.

6.Zookeeper深入进阶

什么是ZAB协议?

​ Zookeeper并没有完全使用Paxos算法,而是使用一种称为Zookeeper Atomic Broadcast(ZAB,Zookeeper 原值消息广播协议)的协议作为其数据一致性的核心算法。

​ ZAB是一种特为为Zookeeper设计的支持崩溃恢复的原子广播协议。Zookeeper主要依赖ZAB来实现分布式数据的一致性,基于ZAB协议,Zookeeper实现了准备模式的系统架构保持集群副本中的数据一致性。

ZAB协议的原理

​ Zab协议要求每个Leader都要经历三个节点:

  • 发现

    ​ 要求Zookeeper集群必须选举出一个Leader进程,同时Leader维护一个Follower可用客户端列表,将来客户端可以和这些Follower节点进行通信。

  • 同步

    ​ Leader要负责将本身的数据与Follower完成同步。做到多副本存储。这也是体现CAP中的高可用和分区容错。Follower将队列未处理完的请求消息完成后,写入本地事务日志中。

  • 广播

    ​ Leader可以接受客户端新的事务Proposal请求,将新的Proposal请求广播给所有的Follower。

ZAB协议的核心

​ 定义了事务请求的处理方式:

  1. 所有事物请求必须由一个全局唯一的服务器来协调处理,该服务器称为Leader服务器,其他剩余的服务器为Follower服务器。
  2. Leader服务器负责将一个客户端事务请求,转成一个事务Proposal,并将该Proposal分发给集群中所有的Follower服务器,也就是向所有Follower节点发送数据广播请求(或者数据复制)
  3. 分发之后Leader服务器需要等待所有Follower服务器的返回(Ack请求),在Zab协议中,只要超过过半的Follower服务器进行了正确的反馈之后,那么Leader就会再次向所有的Follower服务器发送commit消息,要求其将上一个事务Proposal进行提交
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值