zookeeper学习笔记
- 1.Zookeeper概念
- 2.Zookeeper命令操作
- 2.1数据模型
- 2.1.1数据结构
- 2.1.2节点类型
- 2.2服务端命令
- 2.3客户端命令-基本命令
- 2.4客户端命令-高级点命令
- 3.Zookeeper JavaAPI操作
- 3.1Cutor介绍
- 3.2Cutor API常用操作-增删改查
- 3.2.1建立连接
- 3.2.2创建节点
- 3.2.3删除节点
- 3.2.4修改节点
- 3.2.5查询节点
- 3.3Cutor API常用操作-Watch事件监听
- 3.3.1概念
- 3.3.2Watch 机制特点
- 3.4分布式锁
- 3.5模拟12306售票案例
- 4.Zookeeper集群搭建
- 4.1上传zookeeper并解压
- 4.2移动解压的文件,并重新命名
- 4.3创建data目录,并将conf下zoo_sample.cfg改名为zoo.cfg
- 4.4配置每一个Zookeeper的dataDir和clientPort
- 4.5配置集群
- 4.5.1在每个zookeeper的data目录下创建一个myid文件,内容分别为1,2,3这个文件 就是记录每个服务器的id
- 4.5.2在每一个zookeeper的zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表
- 4.6启动集群
- 4.7集群角色
1.Zookeeper概念
Zookeeper 是一个分布式协调服务的开源框架。 主要用来解决分布式集群中应用系统的一致性问题,例如怎样避免同时操作同一数据造成脏读的问题。
ZooKeeper 本质上是一个分布式的小文件存储系统。 提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。
诸如:
- 统一命名服务
- 分布式配置管理
- 分布式消息队列
- 分布式锁(编码实现可以)
- 分布式协调等功能
2.Zookeeper命令操作
2.1数据模型
2.1.1数据结构
- Znode 兼具文件和目录两种特点。既像文件一样维护着数据、元信息、 ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分,并可以具有子 Znode。用户对 Znode 具有增、删、改、查等操作(权限允许的情况下)。
- Znode 具有原子性操作, 读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的 ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的
操作。 - Znode 存储数据大小有限制。 ZooKeeper 虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据, 通常以 KB 为大小单位。 ZooKeeper 的服务器和客户端都被设计为严格检查并限制每个 Znode 的数据大小至多 1M,当时常规使用中应该远小于此值。
- Znode 通过路径引用, 如同 Unix 中的文件路径。 路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在ZooKeeper 中,路径由Unicode 字符串组成,并且有一些限制。字符串"/zookeeper"用以保存管理信息,比如关键配额信息。
2.1.2节点类型
Znode 有两种,分别为临时节点和永久节点。
节点的类型在创建时即被确定,并且不能改变。
-
临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除。 临时节点不允许拥有子节点。
-
永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
-
Znode 还有一个序列化的特性,如果创建的时候指定的话,该 Znode 的名字后面会自动追加一个不断增加的序列号。 序列号对于此节点的父节点来说是唯一的, 这样便会记录每个子节点创建的先后顺序。 它的格式为“ %10d” (10 位数字,没有数值的数位用 0 补充,例如“ 0000000001” )。
2.2服务端命令
2.3客户端命令-基本命令
服务端启动
客户端启动
./zkCli.sh -server localhost:2181
退出
quit
查看
ls /
创建
create /appl cj
获取数据
get /appl
设置数据
set /appl 55
删除
delete /appl/55
删除全部
deleteall
help
2.4客户端命令-高级点命令
创建临时节点
create -e /app1
创建顺序节点
create -s /app1
创建临时顺序节点
create -es /app1
查看节点信息
ls -s /
3.Zookeeper JavaAPI操作
3.1Cutor介绍
官网
3.2Cutor API常用操作-增删改查
3.2.1建立连接
引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jq</groupId>
<artifactId>curator-zk</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.jq.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Test;
public class CuratorTest {
/**
* 建立连接
*/
@Test
public void testConnect(){
/**
* 第一种方式
* @param connectString 连接字符串,zk server地址和端口
* @param sessionTimeoutMs 会话超时时间
* @param connectionTimeoutMs 连接超时时间
* @param retryPolicy 重试策略
*/
// //重试策略
// ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
// CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.56.10:2181", 60 * 1000, 15 * 1000, retryPolicy);
// client.start();
// //开启连接
/**
* 第二种方式
* */
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("192.168.56.10:2181")
.sessionTimeoutMs(60 * 1000)
.connectionTimeoutMs(15 * 1000)
.retryPolicy(retryPolicy).namespace("cj").build();
client.start();
}
}
3.2.2创建节点
package com.jq.curator;
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.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
public class CuratorTest {
private CuratorFramework client;
/**
* 建立连接
*/
@Before
public void testConnect(){
/**
* 第一种方式
* @param connectString 连接字符串,zk server地址和端口
* @param sessionTimeoutMs 会话超时时间
* @param connectionTimeoutMs 连接超时时间
* @param retryPolicy 重试策略
*/
// //重试策略
// ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
// CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.56.10:2181", 60 * 1000, 15 * 1000, retryPolicy);
// client.start();
// //开启连接
/**
* 第二种方式
* */
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(3000,10);
client = CuratorFrameworkFactory.builder()
.connectString("192.168.56.10:2181")
.sessionTimeoutMs(60 * 1000)
.connectionTimeoutMs(15 * 1000)
.retryPolicy(retryPolicy).namespace("cj").build();
client.start();
}
/**
* 创建节点:create 持久,临时,顺序 数据
* 1. 基本创建
* 2. 创建节点带有数据
* 3.设置节点类型
* 4.创建带有多级节点 /app1/p1
*/
@Test
public void testCreate() throws Exception {
//1.基本创建
//如果没有指定数据,默认为当前客户端的ip地址作为数据
//String path= client.create().forPath("/app1");
//2. 创建节点带有数据
//String path= client.create().forPath("/app2","hello app2".getBytes());
//3.设置节点类型
//String path= client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");
//4.创建带有多级节点 /app4/p1
String path= client.create().creatingParentsIfNeeded().forPath("/app4/p1");
System.out.println(path);
}
@After
public void close(){
if (client!=null){
client.close();
}
}
}
3.2.3删除节点
/**
* 删除节点:delete,deleteall
* 1. 删除单个节点
* 2.删除带有子节点的节点
* 3.必须成功删除
* 4.回调
*/
@Test
public void testDelete() throws Exception {
//1.删除单个节点
client.delete().forPath("/app1");
}
@Test
public void testDelete2() throws Exception {
//2.删除带有子节点的节点
client.delete().deletingChildrenIfNeeded().forPath("/app4");
}
@Test
public void testDelete3() throws Exception {
//3.必须成功删除
client.delete().guaranteed().forPath("/app2");
}
@Test
public void testDelete4() throws Exception {
//4.回调
BackgroundCallback backgroundCallback = new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
System.out.println("我被删除了");
System.out.println(curatorEvent);
}
};
client.delete().guaranteed().inBackground(backgroundCallback).forPath("/dubbo");
}v
3.2.4修改节点
/**
* 修改节点:
* 1. 修改数据
* 2.根据版本修改
*/
@Test
public void testSet() throws Exception {
client.setData().forPath("/app1","jeee".getBytes());
}
@Test
public void testSetForVersion() throws Exception {
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("/app1");
int version = stat.getVersion();
System.out.println(version);
client.setData().withVersion(version).forPath("/app1","jeee2".getBytes());
}
3.2.5查询节点
/**
* 查询节点:
* 1. 查询数据:get
* 2.查询子节点: ls
* 3. 查询节点的状态信息: ls-s
* @throws Exception
*/
@Test
public void testGet1() throws Exception {
//1. 查询数据:get
byte[] bytes = client.getData().forPath("/app1");
System.out.println(new String(bytes));
}
@Test
public void testGet2() throws Exception {
//2.查询子节点: ls
List<String> path = client.getChildren().forPath("/app4");
System.out.println(path);
}
@Test
public void testGet3() throws Exception {
//3. 查询节点的状态信息: ls-s
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("/app1");
System.out.println(stat);
}
3.3Cutor API常用操作-Watch事件监听
3.3.1概念
ZooKeeper 提供了分布式数据发布/订阅功能,一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能让多个订阅者同时监听某一个主题对象,当这个主题对象自身状态变化时,会通知所有订阅者,使他们能够做出相应的处理。
ZooKeeper 中,引入了 Watcher 机制来实现这种分布式的通知功能。
ZooKeeper 允许客户端向服务端注册一个 Watcher 监听,当服务端的一些事件触发了这个 Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。
触发事件种类很多, 如:节点创建,节点删除,节点改变,子节点改变等。
总的来说可以概括 Watcher 为以下三个过程:
- 客户端向服务端注册 Watcher、
- 服务端事件发生触发 Watcher、
- 客户端回调 Watcher 得到触发事件情况
Curator 引入了Cache来实现对Zookeeper服务端事件的监听
3.3.2Watch 机制特点
- 一次性触发
事件发生触发监听,一个 watcher event 就会被发送到设置监听的客户端,这种效果是一次性的, 后续再次发生同样的事件,不会再次触发。 - 事件封装
ZooKeeper 使用 WatchedEvent 对象来封装服务端事件并传递。WatchedEvent 包含了每一个事件的三个基本属性:通知状态(keeperState), 事件类型(EventType) 和节点路径(path) - event 异步发送
watcher 的通知事件从服务端发送到客户端是异步的。 - 先注册再触发
Zookeeper 中的 watch 机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端。
3.4分布式锁
相比于redis和数据库 性能稳定可靠
分布式锁,这个主要得益于 ZooKeeper 保证了数据的强一致性。
锁服务可以分为两类,一个是保持独占,另一个是控制时序。
所谓保持独占,就是所有试图来获取这个锁的客户端,最终只有一个可以成功获得这把锁。通常的做法是把 zk 上的一个 znode 看作是一把锁,通过 createznode 的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。
控制时序,就是所有试图来获取这个锁的客户端,最终都是会被安排执行,只是有个全局时序了。做法和上面基本类似,只是这/distribute_lock 已经预先存在,客户端在它下面创建临时有序节点
(这个可以通过节点的属性控制:CreateMode.EPHEMERAL_SEQUENTIAL 来指定)。 Zk 的父节点(/distribute_lock)维持一份 sequence,保证子节点创建的时序性,从而也形成了每个客户端的全局时序。
3.5模拟12306售票案例
4.Zookeeper集群搭建
4.1上传zookeeper并解压
tar -zxvf apache-zookeeper-3.5.6-bin.tar.gz
4.2移动解压的文件,并重新命名
mkdir /usr/local/zookper-cluster
cp -r apache-zookeeper-3.5.6-bin /usr/local/zookper-cluster/zookper-1
cp -r apache-zookeeper-3.5.6-bin /usr/local/zookper-cluster/zookper-2
cp -r apache-zookeeper-3.5.6-bin /usr/local/zookper-cluster/zookper-3
4.3创建data目录,并将conf下zoo_sample.cfg改名为zoo.cfg
mkdir /usr/local/zookper-cluster/zookper-1/data
mkdir /usr/local/zookper-cluster/zookper-2/data
mkdir /usr/local/zookper-cluster/zookper-3/data
mv /usr/local/zookper-cluster/zookper-1/conf/zoo_sample.cfg /usr/local/zookper-cluster/zookper-1/conf/zoo.cfg
mv /usr/local/zookper-cluster/zookper-2/conf/zoo_sample.cfg /usr/local/zookper-cluster/zookper-2/conf/zoo.cfg
mv /usr/local/zookper-cluster/zookper-3/conf/zoo_sample.cfg /usr/local/zookper-cluster/zookper-3/conf/zoo.cfg
4.4配置每一个Zookeeper的dataDir和clientPort
分别为2181 2182 2183
修改/usr/local/zookper-cluster/zookper-1/conf/zoo.cfg
vim /usr/local/zookper-cluster/zookper-1/conf/zoo.cfg
clientPort=2181
dataDir=/usr/local/zookper-cluster/zookper-1/data
修改/usr/local/zookper-cluster/zookper-2/conf/zoo.cfg
vim /usr/local/zookper-cluster/zookper-2/conf/zoo.cfg
clientPort=2182
dataDir=/usr/local/zookper-cluster/zookper-2/data
修改/usr/local/zookper-cluster/zookper-3/conf/zoo.cfg
vim /usr/local/zookper-cluster/zookper-3/conf/zoo.cfg
clientPort=2183
dataDir=/usr/local/zookper-cluster/zookper-3/data
4.5配置集群
4.5.1在每个zookeeper的data目录下创建一个myid文件,内容分别为1,2,3这个文件 就是记录每个服务器的id
echo 1 >/usr/local/zookper-cluster/zookper-1/data/myid
echo 2 >/usr/local/zookper-cluster/zookper-2/data/myid
echo 3 >/usr/local/zookper-cluster/zookper-3/data/myid
4.5.2在每一个zookeeper的zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表
集群服务器IP列表如下
vim /usr/local/zookper-cluster/zookper-1/conf/zoo.cfg
vim /usr/local/zookper-cluster/zookper-2/conf/zoo.cfg
vim /usr/local/zookper-cluster/zookper-3/conf/zoo.cfg
server.1=192.168.56.10:2181:3881
server.2=192.168.56.10:2182:3882
server.3=192.168.56.10:2183:3883
解释:server.服务器id=服务器IP地址:服务器之间通信端口:服务器之间投票选举端口
4.6启动集群
/usr/local/zookper-cluster/zookper-1/bin/zkServer.sh start
/usr/local/zookper-cluster/zookper-2/bin/zkServer.sh start
/usr/local/zookper-cluster/zookper-3/bin/zkServer.sh start