分布式协调服务zookeeper

一、应用场景
在这里插入图片描述在这里插入图片描述在这里插入图片描述
其最主要就两个功能:
①管理(存储,读取)用户程序提交的元数据;
②为用户程序提供数据节点监听服务;

二、zookeeper集群结构
半数机制:集群中半数以上机器存活,集群可用。
zookeeper适合装在奇数台机器上!!!在这里插入图片描述
三、安装
1.解压 tar -zxvf zookeeper-3.4.5.tar.gz
2.修改配置文件

1、用hadoop用户操作
cd zookeeper/conf
cp zoo_sample.cfg zoo.cfg
2、vi zoo.cfg
3、添加内容:
dataDir=/home/hadoop/zookeeper/data
dataLogDir=/home/hadoop/zookeeper/log
server.1=slave1:2888:3888 (主机名, 心跳端口、数据端口)
server.2=slave2:2888:3888
server.3=slave3:2888:3888
4、创建文件夹:
cd /home/hadoop/zookeeper/
mkdir -m 755 data
mkdir -m 755 log
5、在data文件夹下新建myid文件,myid的文件内容为:
cd data
vi myid
添加内容:
1

3.将集群下发到其他机器上
scp -r /home/hadoop/zookeeper hadoop@slave2:/home/hadoop/
scp -r /home/hadoop/zookeeper hadoop@slave3:/home/hadoop/
4.修改其他机器的配置文件
到slave2上:修改myid为:2
到slave3上:修改myid为:3
5.启动每台机器
zkServer.sh start
6.查看集群状态
jps(查看进程)
zkServer.sh status(查看集群状态,主从信息)
7.一键启动集群
在这里插入图片描述
四、zookeeper特性
1、Zookeeper:一个leader,多个follower组成的集群
2、全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪个server,数据都是一致的
3、分布式读写,更新请求转发,由leader实施
4、更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行
5、数据更新原子性,一次数据更新要么成功,要么失败
6、实时性,在一定时间范围内,client能读到最新数据

五、zookeeper数据结构
1、层次化的目录结构,命名符合常规文件系统规范(见下图)
在这里插入图片描述
2、每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识
3、节点Znode可以包含数据和子节点(但是EPHEMERAL类型的节点不能有子节点)
4、客户端应用可以在节点上设置监视器
5、节点类型
①Znode有两种类型:
短暂(ephemeral)(断开连接自己删除)
持久(persistent)(断开连接不删除)
②Znode有四种形式的目录节点(默认是persistent )
PERSISTENT
PERSISTENT_SEQUENTIAL(持久序列/test0000000019 )
EPHEMERAL
EPHEMERAL_SEQUENTIAL
③创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点维护
在这里插入图片描述
④在分布式系统中,顺序号可以被用于为所有的事件进行全局排序,这样客户端可以通过顺序号推断事件的顺序

六、zookeeper命令行操作
运行 zkCli.sh –server 进入命令行工具

1、使用 ls 命令来查看当前 ZooKeeper 中所包含的内容:
[zk: 202.115.36.251:2181(CONNECTED) 1] ls /
#监听这个子节点的变化
[zk: localhost:2181(CONNECTED) 4] ls /zk watch
2、创建一个新的 znode ,使用 create /zk myData 。这个命令创建了一个新的 znode 节点“ zk ”以及与它关联的字符串:
[zk: 202.115.36.251:2181(CONNECTED) 2] create /zk "myData“
3、我们运行 get 命令来确认 znode 是否包含我们所创建的字符串:
[zk: 202.115.36.251:2181(CONNECTED) 3] get /zk
[zk: localhost:2181(CONNECTED) 4] get /zk watch
#监听这个节点的变化,当另外一个客户端改变/zk时,它会打出下面的
#WATCHER::
#WatchedEvent state:SyncConnected type:NodeDataChanged path:/zk
4、下面我们通过 set 命令来对 zk 所关联的字符串进行设置:
[zk: 202.115.36.251:2181(CONNECTED) 4] set /zk "zsl“
5、下面我们将刚才创建的 znode 删除:
[zk: 202.115.36.251:2181(CONNECTED) 5] delete /zk
6、删除节点:rmr
[zk: 202.115.36.251:2181(CONNECTED) 5] rmr /zk

七、zookeeper-api应用
功能 描述
create 在本地目录树中创建一个节点
delete 删除一个节点
exists 测试本地是否存在目标节点
get/set data 从目标节点上读取 / 写数据
get/set ACL 获取 / 设置目标节点访问控制列表信息
get children 检索一个子节点上的列表
sync 等待要被传送的数据

public class SimpleZkClient {

	private static final String connectString = "mini1:2181,mini2:2181,mini3:2181";
	private static final int sessionTimeout = 2000;

	ZooKeeper zkClient = null;

	@Before
	public void init() throws Exception {
		zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
			@Override
			public void process(WatchedEvent event) {
				// 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
				System.out.println(event.getType() + "---" + event.getPath());
				try {
				 //因为zk的watch只能用于监听一次,所以需要在下次被触发时,再次注册监听,因此就有了这句。然而此处一旦发生异常则会导致到监听中断。
					zkClient.getChildren("/", true);
				} catch (Exception e) {
				}
			}
		});
	}

	/**
	 * 数据的增删改查
	 * 
	 * @throws InterruptedException
	 * @throws KeeperException
	 */
	// 创建数据节点到zk中
	public void testCreate() throws KeeperException, InterruptedException {
		// 参数1:要创建的节点的路径 参数2:节点的数据 参数3:节点的权限 参数4:节点的类型
		String nodeCreated = zkClient.create("/eclipse", "hellozk".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		//上传的数据可以是任何类型,但都要转成byte[]
	}

	//判断znode是否存在
	@Test	
	public void testExist() throws Exception{
		Stat stat = zkClient.exists("/eclipse", false);
		System.out.println(stat==null?"not exist":"exist");
	}
	
	// 获取子节点
	@Test
	public void getChildren() throws Exception {
		List<String> children = zkClient.getChildren("/", true);
		for (String child : children) {
			System.out.println(child);
		}
		Thread.sleep(Long.MAX_VALUE);
	}

	//获取znode的数据
	@Test
	public void getData() throws Exception {
		byte[] data = zkClient.getData("/eclipse", false, null);
		System.out.println(new String(data));
	}
	
	//删除znode
	@Test
	public void deleteZnode() throws Exception {
		//参数2:指定要删除的版本,-1表示删除所有版本
		zkClient.delete("/eclipse", -1);
	}
	
	//更新znode数据
	@Test
	public void setData() throws Exception {
		zkClient.setData("/app1", "imissyou angelababy".getBytes(), -1);
		byte[] data = zkClient.getData("/app1", false, null);
		System.out.println(new String(data));
	}
}

八、zookeeper客户端内的守护线程及监听机制
在这里插入图片描述
九、分布式应用系统服务器上下线动态感知
在这里插入图片描述
##Server

public class DistributedServer {
	private static final String connectString = "mini1:2181,mini2:2181,mini3:2181";
	private static final int sessionTimeout = 2000;
	private static final String parentNode = "/servers";
	private ZooKeeper zk = null;

	/**
	 * 创建到zk的客户端连接
	 * 
	 * @throws Exception
	 */
	public void getConnect() throws Exception {
		zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
			@Override
			public void process(WatchedEvent event) {
				// 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
				System.out.println(event.getType() + "---" + event.getPath());
			}
		});

	}

	/**
	 * 向zk集群注册服务器信息
	 * @param hostname
	 * @throws Exception
	 */
	public void registerServer(String hostname) throws Exception {
		String create = zk.create(parentNode + "/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
		System.out.println(hostname + "is online.." + create);
	}

	/**
	 * 业务功能
	 * @throws InterruptedException
	 */
	public void handleBussiness(String hostname) throws InterruptedException {
		System.out.println(hostname + "start working.....");
		Thread.sleep(Long.MAX_VALUE);
	}

	public static void main(String[] args) throws Exception {
		// 获取zk连接
		DistributedServer server = new DistributedServer();
		server.getConnect();
		// 利用zk连接注册服务器信息
		server.registerServer(args[0]);
		// 启动业务功能
		server.handleBussiness(args[0]);
	}
}

##Client

public class DistributedClient {
	private static final String connectString = "mini1:2181,mini2:2181,mini3:2181";
	private static final int sessionTimeout = 2000;
	private static final String parentNode = "/servers";
	// 注意:加volatile的意义是为了在多线程中使用同一变量时保持数据一致性
	private volatile List<String> serverList;
	private ZooKeeper zk = null;

	/**
	 * 创建到zk的客户端连接
	 * @throws Exception
	 */
	public void getConnect() throws Exception {
		zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
			@Override
			public void process(WatchedEvent event) {
				// 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
				try {
					//重新更新服务器列表,并且注册了监听
					getServerList();
				} catch (Exception e) {
				}
			}
		});
	}

	/**
	 * 获取服务器信息列表
	 * @throws Exception
	 */
	public void getServerList() throws Exception {
		// 获取服务器子节点信息,并且对父节点进行监听
		List<String> children = zk.getChildren(parentNode, true);
		// 先创建一个局部的list来存服务器信息
		List<String> servers = new ArrayList<String>();
		for (String child : children) {
			// child只是子节点的节点名
			byte[] data = zk.getData(parentNode + "/" + child, false, null);
			servers.add(new String(data));
		}
		// 把servers赋值给成员变量serverList,已提供给各业务线程使用
		serverList = servers;
		//打印服务器列表
		System.out.println(serverList);
	}

	/**
	 * 业务功能
	 * @throws InterruptedException
	 */
	public void handleBussiness() throws InterruptedException {
		System.out.println("client start working.....");
		Thread.sleep(Long.MAX_VALUE);
	}
	
	public static void main(String[] args) throws Exception {
		// 获取zk连接
		DistributedClient client = new DistributedClient();
		client.getConnect();
		// 获取servers的子节点信息(并监听),从中获取服务器信息列表
		client.getServerList();

		// 业务线程启动
		client.handleBussiness();
	}
}

十、分布式共享资源锁
在这里插入图片描述

public class DistributedClientLock {
	// 会话超时
	private static final int SESSION_TIMEOUT = 2000;
	// zookeeper集群地址
	private String hosts = "mini1:2181,mini2:2181,mini3:2181";
	private String groupNode = "locks";
	private String subNode = "sub";
	private boolean haveLock = false;

	private ZooKeeper zk;
	// 记录自己创建的子节点路径
	private volatile String thisPath;

	/**
	 * 连接zookeeper
	 */
	public void connectZookeeper() throws Exception {
		zk = new ZooKeeper(hosts, SESSION_TIMEOUT, new Watcher() {
			public void process(WatchedEvent event) {
				try {
					// 判断事件类型,此处只处理子节点变化事件
					if (event.getType() == EventType.NodeChildrenChanged && event.getPath().equals("/" + groupNode)) {
						//获取子节点,并对父节点进行监听
						List<String> childrenNodes = zk.getChildren("/" + groupNode, true);
						String thisNode = thisPath.substring(("/" + groupNode + "/").length());
						// 去比较是否自己是最小id
						Collections.sort(childrenNodes);
						if (!haveLock && childrenNodes.indexOf(thisNode) == 0) {
							//用于防止在其他节点创建znode的时候,触发该监听器,从而重复获取资源
							haveLock = true;
							//访问共享资源处理业务,并且在处理完成之后删除锁
							doSomething();
							//重新注册一把新的锁
							thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
									CreateMode.EPHEMERAL_SEQUENTIAL);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});

		// 1、程序一进来就先注册一把锁到zk上
		thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
				CreateMode.EPHEMERAL_SEQUENTIAL);
		// wait一小会,便于观察
		Thread.sleep(new Random().nextInt(1000));

		// 从zk的锁父目录下,获取所有子节点,并且注册对父节点的监听
		List<String> childrenNodes = zk.getChildren("/" + groupNode, true);

		//如果争抢资源的程序就只有自己,则可以直接去访问共享资源 
		if (childrenNodes.size() == 1) {
			doSomething();
			thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
					CreateMode.EPHEMERAL_SEQUENTIAL);
		}
	}

	/**
	 * 处理业务逻辑,并且在最后释放锁
	 */
	private void doSomething() throws Exception {
		try {
			System.out.println("gain lock: " + thisPath);
			Thread.sleep(2000);
			// do something
		} finally {
			System.out.println("finished: " + thisPath);
			// 每次处理完业务后需要释放锁即删除注册的临时节点
			zk.delete(this.thisPath, -1);
			haveLock = false;
		}
	}

	public static void main(String[] args) throws Exception {
		DistributedClientLock dl = new DistributedClientLock();
		dl.connectZookeeper();
		Thread.sleep(Long.MAX_VALUE);
	}
}

在这里插入图片描述
十一、获取zk连接的一个主意事项
在这里插入图片描述即在每次new zookeeper之后,最好增加一个状态判断,以防止在后续直接使用时报关于zk客户端连接失败的问题

state = zk.getState();
int i = 0;
while (State.CONNECTED != state && i<3) {
	sleep(1000);
	state = zk.getState();
	i++;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
什么是Zookeeper Zookeeper是一个分布式开源框架,提供了协调分布式应用的基本服务,它向外部应用暴露一组通用服务——分布式同步(Distributed Synchronization)、命名服务(Naming Service)、集群维护(Group Maintenance)等,简化分布式应用协调及其管理的难度,提供高性能的分布式服务ZooKeeper本身可以以单机模式安装运行,不过它的长处在于通过分布式ZooKeeper集群(一个Leader,多个Follower),基于一定的策略来保证ZooKeeper集群的稳定性和可用性,从而实现分布式应用的可靠性。 1、Zookeeper是为别的分布式程序服务的 2、Zookeeper本身就是一个分布式程序(只要有半数以上节点存活,zk就能正常服务) 3、Zookeeper所提供的服务涵盖:主从协调服务器节点动态上下线、统一配置管理、分布式共享锁、统> 一名称服务等 4、虽然说可以提供各种服务,但是zookeeper在底层其实只提供了两个功能: 管理(存储,读取)用户程序提交的数据(类似namenode中存放的metadata);  并为用户程序提供数据节点监听服务Zookeeper集群机制 Zookeeper集群的角色: Leader 和 follower  只要集群中有半数以上节点存活,集群就能提供服务 Zookeeper特性 1、Zookeeper:一个leader,多个follower组成的集群 2、全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪个server,数据都是一致的 3、分布式读写,更新请求转发,由leader实施 4、更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行 5、数据更新原子性,一次数据更新要么成功,要么失败 6、实时性,在一定时间范围内,client能读到最新数据 Zookeeper数据结构 1、层次化的目录结构,命名符合常规文件系统规范(类似文件系统)    2、每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识  3、节点Znode可以包含数据和子节点(但是EPHEMERAL类型的节点不能有子节点) 节点类型  a、Znode有两种类型: 短暂(ephemeral)(create -e /app1/test1 “test1” 客户端断开连接zk删除ephemeral类型节点)  持久(persistent) (create -s /app1/test2 “test2” 客户端断开连接zk不删除persistent类型节点) b、Znode有四种形式的目录节点(默认是persistent ) PERSISTENT  PERSISTENT_SEQUENTIAL(持久序列/test0000000019 )  EPHEMERAL  EPHEMERAL_SEQUENTIAL c、创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点维护          d、在分布式系统中,顺序号可以被用于为所有的事件进行全局排序,这样客户端可以通过顺序号推断事件的顺序 Zookeeper应用场景 数据发布与订阅(配置中心) 发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。例如全局的配置信息,服务服务框架的服务地址列表等就非常适合使用。 负载均衡 这里说的负载均衡是指软负载均衡。在分布式环境中,为了保证高可用性,通常同一个应用或同一个服务的提供方都会部署多份,达到对等服务。而消费者就须要在这些对等的服务器中选择一个来执行相关的业务逻辑,其中比较典型的是消息中间件中的生产者,消费者负载均衡。 消息中间件中发布者和订阅者的负载均衡,linkedin开源的KafkaMQ和阿里开源的 metaq都是通过zookeeper来做到生产者、消费者的负载均衡。这里以metaq为例如讲下: 生产者负载均衡:metaq发送消息的时候,生产者在发送消息的时候必须选择一台broker上的一个分区来发送消息,因此metaq在运行过程中,会把所有broker和对应的分区信息全部注册到ZK指定节点上,默认的策略是一个依次轮询的过程,生产者在通过ZK获取分区列表之后,会按照brokerId和partition的顺序排列组织成一个有序的分区列表,发送的时候按照从头到尾循环往复的方式选择一个分区来发送消息。 消费负载均衡: 在消费过程中,一个消费者会消费一个或多个分区
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值