Zookeeper
文章目录
初始Zookeeper
Zookeeper的安装
- JDK的安装
https://blog.csdn.net/qq_42815754/article/details/82968464?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-82968464-blog-82926514.pc_relevant_aa2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-82968464-blog-82926514.pc_relevant_aa2&utm_relevant_index=3 - zookeeper的安装
Zookeeper的命令操作
数据模型
服务端命令
- 查看状态
- 关闭
- 启动
- 重启
客户端命令
- 启动客户端,连接服务端
./zkCli.sh -server 127.0.0.1:2181
- 连接本机的服务端,无序添加-server参数
- 退出命令
quit
- 查看节点
ls
- 创建与获取节点
- 命令总结
-
、
javaAPI的操作
- cutator的介绍
curator的连接
- 使用CuratorFrameworkFactory工厂创建CuratorFramework方法
- 指定参数
- 两种方式实现连接
- 使用newclient方法
String connectstring="192.168.253.128:2181";
int sessionTimeout=60*1000;
int connnectionTimeout=3*1000;
RetryPolicy retryPolicy=new ExponentialBackoffRetry(1000,2);
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectstring, sessionTimeout, connnectionTimeout, retryPolicy);
// CuratorFramework lmx = CuratorFrameworkFactory.builder().connectString(connectstring).connectionTimeoutMs(3000).
// retryPolicy(retryPolicy).
// namespace("lmx").build();
curatorFramework.start();
- 使用builder构造器
String connectstring="192.168.253.128:2181";
int sessionTimeout=60*1000;
int connnectionTimeout=3*1000;
RetryPolicy retryPolicy=new ExponentialBackoffRetry(1000,2);
// CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectstring, sessionTimeout, connnectionTimeout, retryPolicy);
CuratorFramework lmx = CuratorFrameworkFactory.builder().connectString(connectstring).connectionTimeoutMs(3000).
retryPolicy(retryPolicy).
namespace("lmx").build();
lmx.start();
**namespace参数:创建节点的根目录,下面创建节点的根目录是在lmx目录下创建,指定namespace可以省略不写根目录 **
节点的创建
- 基本创建
//
// 如果不指定存储的数据,在该节点中存取的是ip地址
String app = lmx.create().forPath("/app");
System.out.println(app);
- 创建节点,带有数据
String s = lmx.create().forPath("/app3", "李满祥".getBytes());
System.out.println(s);
- 创建多级节点,使用creatingParentsIfNeeded方法
String s = lmx.create().creatingParentsIfNeeded().forPath("/app4/person", "people".getBytes());
System.out.println(s);
- 设置节点的类型,使用withMode参数
String s = lmx.create().withMode(CreateMode.EPHEMERAL).forPath("s");
System.out.println(s);
节点的查询
- 查询节点内容
// 查询节点内容
byte[] bytes = lmx.getData().forPath("/");
System.out.println(new String(bytes));
~~~
- 查询子节点
~~~java
// 查询子节点
List<String> strings = lmx.getChildren().forPath("/");
System.out.println(strings);
- 查询节点的状态
// 查看节点的状态
Stat stat = new Stat();
byte[] bytes1 = lmx.getData().storingStatIn(stat).forPath("/app");
System.out.println(stat);
修改节点数据
- 修改数据
Stat stat = lmx.setData().forPath("/app", "lmx".getBytes());
System.out.println(new String(lmx.getData().forPath("/app")));
- 根据版本状态,防止多线程或者多用户去修改,在实际开发过程中使用这种方法
// 根据版本修改
Stat stat = new Stat();
lmx.getData().storingStatIn(stat).forPath("/app");
// 获取版本号
int version = stat.getVersion();
lmx.setData().withVersion(version).forPath("/app","wuxiang".getBytes());
删除节点
- 删除单个节点
lmx.delete().forPath("/app");
- 删除带有子节点的节点 deletingChildrenIfNeeded() 函数
lmx.delete().deletingChildrenIfNeeded().forPath("/app");
- 必须成功删除 guaranteed() 函数
lmx.delete().guaranteed().deletingChildrenIfNeeded().forPath("/app");
- 回调
lmx.delete().guaranteed().deletingChildrenIfNeeded().inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
System.out.println("我被删除了");
System.out.println(curatorEvent);
}
}).forPath("/app1");
Watch事件的监听
-
概念
- ZooKeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通
知到感兴趣的客户端上去,该机制是ZooKeeper实现分布式协调服务的重要特性。 - ZooKeeper中引入了Watcher机制来实现了发布/订阅功能能,能够让多个订阅者同时监听某一个对象,当一个对象自身
状态变化时,会通知所有订阅者。 - ZooKeeper原生支持通过注册Watcher来进行事件监听
但是其使用并不是特别方便
需要开发人员自己反复注册Watcher,比较率琐。 - Curator引入了Cache来实现对ZooKeeper服务端事件的监听。
- ZooKeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通
-
ZooKeeper提供了三种watcher:
- NodeCache:只是监听某一个特定的节点
- PathChildrenCache:监控一个ZNode的子节点
- TreeCache:可以监控整个树上的所有节点,类似于PathChildrenCache和NodeCachel的组合
NodeCache的监听
- 实现某一个节点的监听
// dataIsCompressed参数的意思是否压缩数据
NodeCache nodeCache = new NodeCache(clinet, "/test");
// 设置监听器
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("节点发生了改变");
try {
byte[] bytes = nodeCache.getClient().getData().forPath(nodeCache.getPath());
System.out.println(new String(bytes));
}catch (Exception e){
System.out.println("节点发生异常,可能被删除");
e.printStackTrace();
}
}
});
nodeCache.start();
// clinet.getCuratorListenable().removeListener(change);
while (true) {
}
while (true) {
}模拟的是微服务启动
PathChildrenCache的监听
- 监听该节点下面的子节点数据
clinet.delete().guaranteed().forPath("/test1/p1");
clinet.delete().guaranteed().forPath("/test1/p2");
clinet.create().creatingParentsIfNeeded().forPath("/test1/p1");
clinet.create().creatingParentsIfNeeded().forPath("/test1/p2");
PathChildrenCache pathChildrenCache = new PathChildrenCache(clinet,"/test1",true);
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
System.out.println("节点发生了改变");
if (pathChildrenCacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
byte[] data = pathChildrenCacheEvent.getData().getData();
System.out.println(new String(data));
}
}
});
pathChildrenCache.start();
// 模拟微服务的启动状态
while (true){
}
TreeCache的监听
- 可以监听该节点及子节点的数据
TreeCache treeCache = new TreeCache(clinet,"/test1");
treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
System.out.println("节点改变了");
if (treeCacheEvent.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)){
System.out.println(new String(treeCacheEvent.getData().getData()));
}
}
});
treeCache.start();
// 模拟微服务的启动状态
while (true){
}
分布式锁
概念
- 在我们进行单机应用开发,涉及并发同步的时候,我们往往采用synchronized或者Lock的方式来解决多线程间的代码同步问题,
这时多线程的运行都是在同一个VM之下,没有任何问题。 - 但当我们的应用是分布式集群工作的情况下,属于多VM下的工作环境,跨VM之间已经无法通过多线程的锁解决同步问题。
- 那么就需要一种更加高级的锁机制,来处理跨机器的进程之间的数据同步问题一这就是分布式锁。
zk实现分布式锁的原理
- 核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该点。
curator实现分布式锁
- 模仿买票的多线程
public class Ticket12306 implements Runnable {
private int ticket = 10;
private InterProcessMutex interProcessMutex;
public Ticket12306() {
RetryPolicy retryPolicy=new ExponentialBackoffRetry(3*1000,10);
CuratorFramework build = CuratorFrameworkFactory.builder().connectString("192.168.253.128:2181")
.connectionTimeoutMs(3 * 1000)
.retryPolicy(retryPolicy)
.build();
build.start();
interProcessMutex=new InterProcessMutex(build,"/lock");
}
// @SneakyThrows
@Override
public void run() {
while (true) {
try {
interProcessMutex.acquire(3, TimeUnit.SECONDS);//获取锁的时间;
// 获取锁
if (ticket>0){
System.out.println(Thread.currentThread().getName() + "卖票了" + ticket);
ticket--;
}
Thread.sleep(2000);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
interProcessMutex.release();
if (ticket==0){
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 释放锁
}
// 释放锁
}
}
- 关键代码
- 获取锁与释放锁
interProcessMutex.acquire(3, TimeUnit.SECONDS);//获取锁的时间;
interProcessMutex.release();
- 实现卖票
Ticket12306 ticket12306 = new Ticket12306();
Ticket12306 ticket123061 = new Ticket12306();
Thread t1=new Thread(ticket12306,"携程");
Thread t2=new Thread(ticket12306,"飞猪");
t1.start();
t2.start();