Zookeeper基础学习
zookeeper
概述
是一个开源的分布式应用的服务,提供了更高级别的服务,包括:同步、配置维护、分组和命名。提供了优质的高性能、高可用性和严格有序的访问。
应用场景
数据发布/订阅
数据发布/订阅,需要发布者将数据发布到zookeeper的节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
具有两种设置模式:
推模式
服务端主动将数据更新发送给所有订阅的客户端;
拉模式
客户端主动请求获取最新数据。
Zookeeper采用了推拉相结合的模式,客户端向服务端注册自己需要关注的节点,一旦节点数据发生改变,那么服务器就会向相应的客户端推送Watcher事件通知,客户端接收到次通知后没主动到服务端获取最新的数据。
命名服务
Zookeeper可以实现一套分布式全局唯一ID的分配机制。
通过调用Zookeeper节点创建的API接口就可以创建一个顺序节点,并且再API返回值中会返回这个节点的完整名字,利用此特性,可以生成全局id,其步骤如下:
- 客户端根据任务类型,在指定类型的任务下通过调用接口创建一个顺序接节点,如job-
- 创建完成后,会返回一个完整的节点名。如job-000000000001
- 客户端拼接type类型和返回值后,就可以作为全局唯一id,如type2-job-000000000001
分布式协调/通知
Zookeeper中特有的Watcher注册于异步通知机制,能够很好的实现分布式环境下不同机器,甚至不同系统之间的协调与通知,从而实现对数据变更的实时处理,
通常做法是不同的客户端都对Zookeeper上的同一节点进行watcher注册,监听数据节点的变化(包括节点本身和子节点),若数据节点发生变化,那么所有订阅的客户端都能够接受到相应的Watcher通知,并作出通知。
在绝大多数分布式系统中,系统机器件的通信无外乎心跳检测、工作进度汇报和系统调度
分布式锁
分布式锁用于控制分布式系统之间同步访问共享资源的一直方式,可以保证不同系统访问一个或一组资源时的一致性,主要分为排他锁和共享锁。
排他锁又被称为写锁或独占锁,若事务A对数据对象01加上了排他锁,那么整个加所期间,只允许事务A对01进行读取和更新操作。其他任何事务不允许操作。
共享锁被称为读锁,若事务A对数据对象01加上共享锁,那么当前事务只能对01进行读取操作,其他四五也只能对这个数据对象加共享锁,知道该数据对象上的所有共享锁都被释放。
分布式队列
集群环境搭建
| 服务器ip | 主机名 | myid的值 |
|---|---|---|
| ** | node01 | 1 |
| ** | node02 | 2 |
| ** | node03 | 3 |
步骤:
第一步:下载zookeeper的压缩包,
第二步:修改配置文件 conf/zoo_conf

第三步:添加myid配置
在/export/servers/zookeeper-***/zkdatas 创建一个文件,文件名为myid,文件内容为1.
第四步:启动项目


结构类型
数据模型

zk的数据存储是是结构化存储,没有文件和目录的概念,文件和目录被抽象成了节点(node),上图中的每个node称为znode, 每个znode由三个部分组成:
1. stat, 描述该znode的版本权限等信息。
2. data,与该znode关联的数据。
3. children, 该znode下的子节点
zk除了提供api接口外,同样提供了shell接口来进行相应的操作。
节点类型
ZooKeeper节点znode有两种类型,临时节点(ephemeral)和持久节(persistent)。znode的类型在创建时确定并且之后不能再修改。
ephemeral节点在客户端会话结束时,将会被zookeeper删除,并且ephemeral节点不可以有子节点。
persistent节点不依赖与客户端会话,只有当客户端明确要删除该persistent节点时才会被删除。
znode还有一个序列化的特性,如果创建的时候指定的话,该Znode的名字后面会自动追加一个不断增加的序列号,序列号对此此节点的父节点来说是唯一的,它便会记录每个子节点创建的先后顺序,它的格式是'%10d'.
此时就会出现四种类型的Znode节点,分别对应:
- Persisent:永久节点
- Ephemeral:临时节点
- Persisent_sequential:永久节点、序列化
- Ephemeral_sequential:临时节点、序列化
shell命令
| 命令 | 说明 | 参数 |
|---|---|---|
create [-s] [-e] path data acl |
创建Znode | -s指定顺序节点 -e 指定是临时节点,如果无参数,表示永久化节点 |
ls path [watch] |
列出Path下所有子Znode | |
| ls2 path [watch] | 查看path下所有znode以及子Znode的属性 | |
| set path data [version] | 更新节点 | version数据版本 |
| get path [watch] | 获取path对应的Znode的数据和属性 | |
| delete path [version] | 删除节点 | version数据版本 |
| history | 历史记录 |
属性:

dataversion:数据版本号,进行更新操作加一。
cversion: 子节点的版本号
aclVersion:ACL的版本号
cZxid:Znode创建的事务id。
mZxid: Znode被修改的事务id
ctime:创建时间
mtime:更新时间
ephemeralOwner:临时节点的会话id
watch机制

javaAPi操作
推荐使用JavaApi的是一套封装好的zookeeper客户端框架Curator,解决了很多zk客户端底层的细节。
Curator包含的几个包
-
curator-framework:对zookeeper的底层api的一些封装 -
curator-recipes:封装了一些高级特性,如:cache事件监听,选举,分布式锁,分布式计数器。/** * 创建节点 */ @Test void createNode() throws Exception { // 定义要连接的zk的所在虚拟机路径 ExponentialBackoffRetry retry = new ExponentialBackoffRetry(3000, 3); // 获取客户端对象 CuratorFramework client = CuratorFrameworkFactory.newClient(connString, retry); // 开启客户端操作 client.start(); // 创建永久节点 client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/hello1"); // 修改节点数据 client.setData().forPath("/hello1","hello".getBytes()); // 查询节点数据 byte[] bytes=client.getData().forPath("/hello1"); System.out.println(new String(bytes)); client.close(); }@Test public void myWatch() throws Exception { CuratorFramework client = CuratorFrameworkFactory .newClient(connString, new ExponentialBackoffRetry(3000, 0)); client.start(); TreeCache treeCache = new TreeCache(client, "/createNode03"); // 获取监听容器 treeCache.getListenable().addListener(new TreeCacheListener() { @Override public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception { GetDataBuilder data = client.getData(); if(data != null){ switch(treeCacheEvent.getType()){ case NODE_ADDED: System.out.println("监听添加节点------------"); break; case NODE_UPDATED: System.out.println("更新节点数据"); default:break; } } } }); treeCache.start(); Thread.sleep(50000); }

浙公网安备 33010602011771号