zookeeper概念
-
- zookeeper用例管理hadoop, hive ,pig的管理员
- 提供的功能:
- 配置管理
-
- 服务的配置信息放在zookeeper中,修改配置文件后不用重启服务,直接生效,可以管理数百个服务的配置文件。
- 分布式锁
- 集群管理
zookeeper的安装
使用docker進行安裝:
cloud.tencent.com/developer/a… 注意:
本用例安装的是zookeeper3.8版本
docker run -p 2181:2181 --name zookeeper-standalone --restart always -d zookeeper:3.8
docker exec -it zookeeper-standalone /bin/bash
zookeeper命令操作
数据模型
- ZNode節點,可以存放少量数据(1MB),也允许有子节点。
- 节点类型:
- PERSISTENT: 持久节点
- EPHEMERAL:临时节点 -e
- PERSISTENT_SEQUENTIAL 持久化顺序节点 -s
- EPHEMERAL_SEQUENTIAL 临时顺序节点 -es
命令操作:
服务端命令
客户端命令
- 默认创建节点是持久化的。
- 创建临时节点 -e
- create -e
- 临时节点是会话级别的,当服务端关闭后节点就丢失了。
zookeeper的javaApi操作
Curator介绍 & Curator API常见操作
Curator要和zookeeper版本对应起来。
建立链接
使用Curator建立链接
支持命名空间的设置。
添加节点
- 创建路径
- 创建节点+数据: create().forPath(“”,data)
- 默认类型:持久化的。使用withMode()指令节点的类型。
- 临时节点javaClient与zookeeper服务断开会删除。
-如果父节点不存在则创建父节点。
删除
- 删除单个节点: delete().forPath()
- 删除带有子节点的节点
- 必须成功的删除:本质就是重试 -回调:绑定一个回调函数,节点删除之后自动直接回调方法。
修改
- 修改数据
setData().forPath() - 根据版本修改:
setData().withVersion().forPath()
查询
- 查询数据get() :getData().forPath()
- 查询子节点:ls: getChildren().forPath()
- 查询节点状态信息:ls -s: getData().storingStatIn(对象).forPath()
watch监听方法:
概念
当事件发生变更的时候通知到注册的观察者上
javaApi 实现NodeCache模式
- 监听当前节点的增删改查。
- 监听当前节点变化的值
javaApi 实现PathChildrenCache模式
- 监听子节点的增删改查
- 监听子节点数据修改之后的变化的数据
javaApi 实现TreeCache模式
watch 事件的代码实现
以上代码
public class ZookeeperWatchTest {
private CuratorFramework client;
public static Logger log=LogManager.getLogger(ZookeeperWatchTest.class);
/**
* 建立链接
*/
@Before
public void testConnect(){
//方法1:使用构造函数
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
// client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000, retryPolicy);
//方法2:使用build链式编程
client= CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(60*1000)
.connectionTimeoutMs(15*1000)
.retryPolicy(retryPolicy)
.namespace("itheima")
.build();
client.start();
}
//使用NodeCache模式
@Test
public void testNodeCahce() throws InterruptedException {
CuratorCache curatorCache = CuratorCache.build(client, "/app1");
//创建监听
curatorCache.listenable().addListener(new CuratorCacheListener() {
// 第一个参数:事件类型(枚举)
// 第二个参数:节点更新前的状态、数据
// 第三个参数:节点更新后的状态、数据
// 创建节点时:节点刚被创建,不存在 更新前节点 ,所以第二个参数为 null
// 删除节点时:节点被删除,不存在 更新后节点 ,所以第三个参数为 null
// 节点创建时没有赋予值 create /curator/app1 只创建节点,在这种情况下,更新前节点的 data 为 null,获取不到更新前节点的数据
@Override
public void event(Type type, ChildData childData, ChildData childData1) {
switch (type.name()){
case "NODE_CREATED":
if(childData!=null){
System.out.println("创建了节点:"+ new String( childData1.getData()));
}
break;
case "NODE_CHANGED":
if(childData!=null){
System.out.println("更新了节点:"+ new String( childData1.getData()));
}else {
System.out.println("节点第1次赋值");
}
break;
case "NODE_DELETED":
System.out.println(childData.getPath()+"节点已经删除");
break;
default:
break;
}
}
});
//开启监听
curatorCache.start();
//延迟结束
Thread.sleep(1000*60*10);
}
//使用PathChildrenCache模式,方法已经过时
@Test
public void testPathChildrenCache() throws Exception {
PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/app2", true);
//创建监听
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
System.out.println("子节点发生了变化");
PathChildrenCacheEvent.Type type = event.getType();
if(type.equals(PathChildrenCacheEvent.Type.CHILD_ADDED)){
System.out.println("新增子节点"+new String(event.getData().getPath() +":"+new String( event.getData().getData())));
}
if(type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
System.out.println("修改子节点"+new String(event.getData().getPath() +":"+new String( event.getData().getData())));
}
if(type.equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)){
System.out.println("删除子节点"+new String( event.getData().getPath() +":"+new String( event.getData().getData())));
}
}
});
//开启监听set
pathChildrenCache.start();
//延迟结束
Thread.sleep(1000*60*10);
}
/**
* 使TreeCache: 监听自身节点以及后续节点的变化
* @throws InterruptedException
*/
@Test
public void testTreeCache() throws Exception {
TreeCache treeCache = new TreeCache(client, "/app2");
//创建监听
treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent event) throws Exception {
System.out.println("节点发生了变化");
TreeCacheEvent.Type type = event.getType();
if(type.equals(TreeCacheEvent.Type.NODE_ADDED)){
System.out.println("新增节点"+new String(event.getData().getPath() +":"+new String( event.getData().getData())));
}
if(type.equals(TreeCacheEvent.Type.NODE_UPDATED)){
System.out.println("修改节点"+new String(event.getData().getPath() +":"+new String( event.getData().getData())));
}
if(type.equals(TreeCacheEvent.Type.NODE_REMOVED)){
System.out.println("删除节点"+new String( event.getData().getPath() +":"+new String( event.getData().getData())));
}
}
});
//开启监听set
treeCache.start();
//延迟结束
Thread.sleep(1000*60*10);
}
@After
public void destory(){
if(client!=null){
client.close();
}
}
}
分布式锁的实现
概述
- 单机应用并发,设计并发同步,采用synchronized或者lock的方式来解决多线程间的代码同步问题。这是多线程运行在同一个JVM之下,没有问题。
- 跨JVM工作环境,需要更高级的锁机制【跨级机器的进程之间的数据同步问题】——分布式锁
- 基于缓存实现分布式锁:redis: master一点挂了,则可能多个服务都获得分布式锁,不可靠。优点是:性能高。
- 基于zookeeper实现:性能比数据库高,且可靠。
- 基于数据库实现:性能较低。
分布式锁原理
- Curator已经完全实现。
- 客户端连接获取锁是,在lock节点下创建【临时顺序】节点:这样如果client1客户端挂了,则临时 节点会自动删除。
使用Curator实现分布式锁API
案例:模拟12306卖票
具体代码
public class Ticket12306 implements Runnable{
private InterProcessMutex lock;
public Ticket12306(){
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
// client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000, retryPolicy);
//方法2:使用build链式编程
CuratorFramework client= CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(60*1000)
.connectionTimeoutMs(15*1000)
.retryPolicy(retryPolicy)
.build();
client.start();
lock=new InterProcessMutex(client, "/lock");
}
private int tickQty=10;
@Override
public void run() {
while (true){
try {
lock.acquire(2, TimeUnit.SECONDS);
if(tickQty>0){
tickQty--;
System.out.println("剩余票数:" +tickQty+ Thread.currentThread().getName()+"购票成功");
Thread.sleep(100);
}
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
try {
lock.release();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
Zookeeper 集群搭建
zookeeper介绍
- 如果启动了3台,则3号最大,且获得票数超过半数,所以是leader,但是如果后来再有4号机器进入,因为已经有leader(3号),所以不会重新进行选举,4号此时不会成为新的leader。
集群搭建:
集群故障测试
集群角色
- Leader:处理事务请求(增删改)
- Follower: 1,处理非事务请求,转发事务请求给Leader服务器2,产出Leader选举投票。
- Observer:处理非事务请求,转发事务请求给Leader服务器.【非实物请求较多,为了分单foller的压力,所以存在Observer角色。】
链接zookeeper集群
public void testConnect(){
//方法1:使用构造函数
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
// client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000, retryPolicy);
//方法2:使用build链式编程
client= CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183")
.sessionTimeoutMs(60*1000)
.connectionTimeoutMs(15*1000)
.retryPolicy(retryPolicy)
.namespace("itheima")
.build();
client.start();
}