zookeeper基本使用
1. zookeeper简介
Zookeeper是一个分布式协调服务, 是为用户的分布式应用程序提供协调服务
- Zookeeper是为别的分布式程序服务的
- Zookeeper本身就是一个分布式程序(只要有半数以上节点存活,zk就能正常服务)
- Zookeeper所提供的服务涵盖:主从协调、服务器节点动态上下线、统一配置管理、分布式共享锁、统一名称服务……
- 虽然说可以提供各种服务,但是zookeeper在底层其实只提供了两个功能:
管理(存储,读取)用户程序提交的数据;
并为用户程序提供数据节点监听服务;
2. zookeeper安装配置及集群搭建
2.1 下载 --> 官网
2.2 上传到linux后,解压
2.3 进入zookeeper目录,其根目录下几个目录如下
bin目录: 一些操作zookeeper的指令
zkServer.sh [start | stop | status | restart ]
conf目录: 配置文件
# 将conf目录下的 zoo_sample.cfg 复制一份为 zoo.cfg , zookeeper服务器启动默认加载zoo.cfg
cp ./conf/zoo_sample.cfg ./zoo.cfg
# 修改zoo.cfg中的配置, 修改内容如下
dataDir=/root/work/zookeeper/data # 配置数据目录,创建一个data目录,将其路径填入即可
dataLogDir=/root/work/zookeeper/log # 同理(可以不配置此项)
# 配置集群节点,server.x中 x 为节点id, slave1 为节点服务器主机名,或者ip地址
# 节点id配置, 在data目录下创建文件名为myid的文件,在文件中直接写入id即可,例如 slave1的myid文件中写 1
server.1=slave1:2888:3888 (主机名, 心跳端口、数据端口)
server.2=slave2:2888:3888
server.3=slave3:2888:3888
zookeeper集群机制为半数机制, 即集群中半数以上机器存活集群才可用。
启动集群,把每个节点的zookeeper
zkServer.sh start
查看状态, 主从信息
zkServer.sh status
3. zookeeper 结构命令
3.1 zookeeper 特性
1、Zookeeper:一个leader,多个follower组成的集群
2、全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪个server,数据都是一致的
3、分布式读写,更新请求转发,由leader实施
4、更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行
5、数据更新原子性,一次数据更新要么成功,要么失败
6、实时性,在一定时间范围内,client能读到最新数据
3.2 zookeeper 数据结构
1、层次化的目录结构,命名符合常规文件系统规范(见下图)
2、每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识
3、节点Znode可以包含数据和子节点(但是EPHEMERAL类型的节点不能有子节点)
4、客户端应用可以在节点上设置监视器
3.3 zookeeper节点类型
1、Znode有两种类型:
短暂(ephemeral)(断开连接自己删除)
持久(persistent)(断开连接不删除)
2、Znode有四种形式的目录节点(默认是persistent )
PERSISTENT
PERSISTENT_SEQUENTIAL(持久序列,例如 create -s /test 生成znode为 /test0000000001 )
EPHEMERAL
EPHEMERAL_SEQUENTIAL
3、创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点维护
4、在分布式系统中,顺序号可以被用于为所有的事件进行全局排序,这样客户端可以通过顺序号推断事件的顺序
3.4 znode节点元数据stat,例子
cZxid = 0xb00000003 内部数据更新时的事务控制的编号,创建一个数据时,内部就会有一个事务编号
ctime = Tue Feb 13 08:32:40 CST 2018 创建时间
mZxid = 0xb00000005 修改事务的编号
mtime = Tue Feb 13 08:34:47 CST 2018 修改时间
pZxid = 0xb00000003 持久化事务编号
cversion = 0 创建版本号
dataVersion = 1 数据版本号
aclVersion = 0 权限版本
ephemeralOwner = 0x0
dataLength = 5 数据长度
numChildren = 0 子节点数
3.5zookeeper 命令行操作
运行 zkCli.sh –server ip:port 进入命令行工具
[zk: localhost:2181(CONNECTED) 0] help # help 指令查看帮助
ZooKeeper -server host:port cmd args
connect host:port # 连接到zookeeper集群中其他节点
get path [watch] # 获取 path 的数据, wath (只监听一次)选项,监听 znode 数据变化
ls path [watch] # 获取 path 下的 znode 列表
set path data [version] # 修改 path 下znode的data
rmr path # 递归删除 path 节点及其子节点
delquota [-n|-b] path
quit # 退出 zookeeper 客户端
printwatches on|off
create [-s] [-e] path data acl # 创建节点, -s 设置顺序标识 -e 创建节点为短暂类型 acl 权限
stat path [watch] # 查看 path 的元数据
close
ls2 path [watch]
history
listquota path
setAcl path acl
getAcl path
sync path
redo cmdno
addauth scheme auth
delete path [version] # 删除path节点,只能用于删除叶子节点
setquota -n|-b val path
4. java操作zookeeper客户端
4.1 搭建环境
- 方法一: 建立一个maven项目,在pom.xml中添加zookeeper客户端依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
</dependency>
- 方法二: 建立一个普通的java项目,创建一个lib目录, 将zookeeper解压后的根目录下的 zookeeper-3.4.10.jar 和 根目录下的lib目录下的jar包一起拷贝到java项目的lib目录下,并将他们导入到项目环境中。
4.2 基本使用
org.apache.zookeeper.Zookeeper是客户端入口主类,负责建立与server的会话
它提供了表 1 所示几类主要方法 :
功能 | 描述 |
---|---|
create | 在本地目录树中创建一个节点 |
delete | 删除一个节点 |
exists | 测试本地是否存在目标节点 |
get/set data | 从目标节点上读取 / 写数据 |
get/set ACL | 获取 / 设置目标节点访问控制列表信息 |
get children | 检索一个子节点上的列表 |
sync | 等待要被传送的数据 |
4.2.1 基本操作,增删改查
package com.lunarku.bigdata.zk;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Test;
/**
* java代码操作zookeeper测试
*/
public class SimpleZkClient {
// zookeeper集群服务器ip端口
private static final String CONNECT_STRING=
"192.168.25.141:2181,192.168.25.142:2181,192.168.25.143:2181";
private static final int SESSION_TIMEOUT = 2000;
// 用来测试创建 /eclipse 节点
private static final String PATH = "/eclipse";
public ZooKeeper zkClient = null;
/**
* 初始化 Zookeeper 对象
* @throws Exception
*/
@Before
public void init() throws Exception {
// 第三个参数为回调函数
zkClient = new ZooKeeper(CONNECT_STRING, SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 收到事件通知后的回调函数
System.out.println(event.getType() + "---" + event.getPath());
}
});
}
/**
* 创建 znode
* @throws KeeperException
* @throws InterruptedException
*/
@Test
public void testCreate() throws KeeperException, InterruptedException {
// 参数1: 要创建的节点的路径 参数2: 节点数据 参数3: 节点权限 参数4:节点的类型
zkClient.create(PATH, "zk-test".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zkClient.create(PATH + "/node1", "zk-node1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zkClient.create(PATH + "/node2", "zk-node2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* 获取PATH下的子znode列表
* @throws KeeperException
* @throws InterruptedException
*/
@Test
public void testGetChildren() throws KeeperException, InterruptedException {
List<String> children = zkClient.getChildren(PATH, true);
for(String child : children) {
System.out.println(child);
}
}
/**
* 获取PATH的znode的数据data
* @throws KeeperException
* @throws InterruptedException
*/
@Test
public void testGetData() throws KeeperException, InterruptedException {
byte[] data = zkClient.getData(PATH, true, null);
System.out.println(new String(data));
}
/**
* 判断节点路径是否存在
* @throws KeeperException
* @throws InterruptedException
*/
@Test
public void testExist() throws KeeperException, InterruptedException {
// 存在则返回Stat 元数据对象, 不存在返回null
Stat exists = zkClient.exists(PATH, false);
System.out.println(exists);
System.out.println(exists == null ? "not exist" : "exist");
}
/**
* 删除叶子节点, 若路径参数不为叶子节点,删除会报错.
* Zookeeper对象 不支持删除非叶子节点,只能删除完子节点,才能删除父节点
* @throws InterruptedException
* @throws KeeperException
*/
@Test
public void testDelete() throws InterruptedException, KeeperException {
// 参数1: 删除的节点路径 参数2: 指定删除的版本, -1表示删除所有版本
zkClient.delete(PATH+"/node1", -1);
}
/**
* 修改路径节点数据
* @throws Exception
* @throws InterruptedException
*/
@Test
public void testSetZnode() throws Exception, InterruptedException {
zkClient.setData(PATH, "modify".getBytes(), -1);
byte[] data = zkClient.getData(PATH, false, null);
System.out.println(new String(data));
}
}
4.2.2 模拟系统服务器上下线动态感知
原理:
服务器端:服务器上线就在zookeeper的/server路径下创建一个EPHEMERAL_SEQUENTIAL类型的znode,znode的data值设为服务器名字。由于znode为短暂类型,当服务器下线,则对应的znode会被自动删除。
客户端: 获取与zookeeper的连接,获取zookeeper的/server路径下的znode,这些znode就为在线的服务器,并对/server路径设置监听,当/server路径下的节点发生变化,则重新获取/server下的节点列表,更新在线服务器列表。
客户端: DistributeClient
服务端 : DistributedServer
4.2.3 模拟实现分布式锁
原理:每个服务器上线在zookeeper的/server路径下生成一个带序号的自动递增短暂节点,客户端使用时取序号最小的服务器,使用完毕后删除该序号最小服务器节点,然后重新生成该服务器节点,下次使用继续取服务器序号最小节点,效果类似轮询。
DistributeClientLock