Zookeeper
1. 初步认识Zookeeper
zookeeper是一个开源的分布式协调服务,是由雅虎创建的,基于google chubby(分布式锁).分布式数据一致性的解决方案.
1.1 zookeeper能做什么?
数据的发布订阅(配置中心.比如:disconf),负载均衡(dubbo+zookeeper),命名服务,master选举(kafka,hadoop,hbase),分布式队列strong text
1.2 zookeeper的特性
- 顺序性:从同一个客户端发起的请求,最终会严格按照顺序被应用到zookeeper中.
- 原子性:所有的事务请求的处理结果在整个集群中的所有机器上的应用情况是一致的,也就是说,要么整个集群中的所有机器都成功应用了某一事务,要么全都不应用.
- 可靠性:一旦服务器应用了某一事务数据,并且对客户端做出了响应,那么这个数据在整个集群中一定是同步的并且保留下来的.
- 实时性:一旦一个事务被成功应用,就能够立即从客服端读取到事务变更后的最新数据状态(zookeeper仅仅保证在一定时间内,近实时).
2. 安装zookeeper 单机/集群
1.下载安装包,选择合适版本下载.下载地址
2…此处省略若干个字
Zookeeper中的一些概念
1.数据模型 :zookeeper中的数据模型和文件系统类似,每一个节点成为 znode,是zookeeper中的最小数据单元.每一个znode上都可以保存数据和挂在子节点.从而构成一个层次化的属性结构.
2.节点特性:
- 持久化节点:节点创建后会一直存在zk服务器上,直到主动删除.
- 持久化有序节点:每个节点都会为它的一级子节点维护一个顺序.
- 临时节点:临时节点的生命周期和客户端会话保持一致,当前客户端会话失效,该节点自动清理,
- 临时有序节点:在临时节点上多了一个顺序性特性
3.Watch:zookeeper提供了分布式数据发布/订阅,zookeeper允许客户端向服务器注册一个watcher监听.当服务端的节点触发指定事件的时候会出发watcher.服务端会向客户端发送一个事件通知.[watcher的通知是一次性的,一旦触发一次通知后,该watcher就失效了]
4.ACL:zookeeper提供控制节点访问权限的功能,用于有效的保证zookeeper中数据的安全性.避免误操作而导致系统出现重大事故.[CREATE/READ/WRIT/DELETE/ADMIN]
ZK的命令操作
- create [-s] [-e] path dataa acl
-s表示是否有序
-e表示是否为临时节点
默认情况下是持久化节点
JAVA API的使用
原生的api
1.导入jar包
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.5</version>
</dependency>
zkclient
1.导入jar包
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
2.代码说明api
public class ZKClientApiOpeatorDemo {
private final static String CONNECTSTRING= "39.105.157.149:2181," +
"39.105.157.149:2182," +
"39.105.157.149:2183," +
"39.105.157.149:2184";
private static ZkClient zkClient=new ZkClient(CONNECTSTRING,4000);
public static void main(String[] args) throws InterruptedException {
/**
* CreateMode.PERSISTENT;//持节点
* CreateMode.PERSISTENT_SEQUENTIAL;//持久有序节点;
* CreateMode.EPHEMERAL;//临时节点
* CreateMode.EPHEMERAL_SEQUENTIAL;//临时有序节点
*/
//创建节点
//zkClient.create("/node1", "123", CreateMode.EPHEMERAL);
//创建临时节点
//zkClient.createEphemeral("/node1",true);
//递归创建节点
// zkClient.createPersistent("/node1/node2/node3",true);
//删除节点
//zkClient.delete("/node1/node2/node3");
//递归删除
// zkClient.deleteRecursive("/node1/node2");
//根据版本号删除
//zkClient.delete("/node1", 1);
//获取子节点
//List<String> strings = zkClient.getChildren("/");
//写内容
// zkClient.writeData("/node1", "123");
//根据版本号写内容
//zkClient.writeData("/node1","123",1);
//读内容
// Object data = zkClient.readData("/node1");
//....还有很多api
//订阅
//订阅dataChanges
/* zkClient.subscribeDataChanges("/node1", new IZkDataListener() {
@Override//数据修改
public void handleDataChange(String s, Object o) throws Exception {
System.out.println("节点名称:"+s+"----->节点修改后的值:"+o);
}
@Override //数据删除
public void handleDataDeleted(String s) throws Exception {
}
});
zkClient.writeData("/node1", "node");
TimeUnit.SECONDS.sleep(2);
*/
//还有很多订阅......................
}
}
curator 目前用的最广泛的
Curator本身是Netflix公司开源的zookeeper客户端
Curator提供了各种应用场景的实现封装
Curator-framework 提供了fluent风格api (链式操作)
Curator-replice 提供了实现封装
1.导入jar包
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.11.0</version>
</dependency>
</
2.代码说明api
public class CuratorSessionDemo {
private final static String CONNECTSTRING= "39.105.157.149:2181," +
"39.105.157.149:2182," +
"39.105.157.149:2183," +
"39.105.157.149:2184";
//创建连接
public static CuratorFramework getInstance(){
//curator-framework 建立连接 两种方式
/**
* curator连接的重试策略:
* ExponentialBackoffRetry 衰减重试
* RetryNTimes 指定最大重试次数
* RetryOneTime 仅重试一次
* RetryUnitilElapsed 一直重试直到规定时间
*/
//第一种 静态方法
/* CuratorFramework curatorFramework1= CuratorFrameworkFactory.newClient("CONNECTSTRING", 1000,
1000,
new ExponentialBackoffRetry(1000, 1000));//ExponentialBackoffRetry重试策略的一种
curatorFramework1.start();*/
//第二种 fluent风格
CuratorFramework curatorFramework2 = CuratorFrameworkFactory.builder().connectString(CONNECTSTRING)
.sessionTimeoutMs(1000)
.connectionTimeoutMs(1000)
.retryPolicy(new ExponentialBackoffRetry(1000, 1000))
//.namespace("/curator")//命名空间,可以制定根节点 后续都在这个节点下 比如创建节点"/aaa",它的路径就是/curator/aaa
.build();
curatorFramework2.start();
return curatorFramework2;
}
public static void main(String[] args) throws Exception {
CuratorFramework curatorFramework = CuratorSessionDemo.getInstance();
//fluent风格 有很多方法 可以一直根据需要点下去
//创建节点
// String s = curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT)
//.forPath("/curator/node1", "123".getBytes());
//读取节点内容 //storingStatIn 可以获取状态信息
// Stat stat = new Stat();
// byte[] bytes = curatorFramework.getData().storingStatIn(stat).forPath("/curator/node1");
//修改内容
//curatorFramework.setData().forPath("/curator/node1", "456".getBytes());
//删除节点
// curatorFramework.delete().deletingChildrenIfNeeded().forPath("/curator/node1");
/**
* 异步操作
*/
/* ExecutorService executorService = Executors.newFixedThreadPool(1);
//由于是异步的 看不到打印 设置一个计数器 让线程等待
CountDownLatch countDownLatch = new CountDownLatch(1);
curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
.inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
System.out.println(Thread.currentThread().getName()+"-->resultCode"+curatorEvent.getResultCode()+
"-->"+curatorEvent.getType());
countDownLatch.countDown();;
}
},executorService).forPath("/ajc","1523".getBytes());
countDownLatch.await();*/
/**
* 事务操作
*/
/* Collection<CuratorTransactionResult> commit = curatorFramework.inTransaction().create().withMode(CreateMode.EPHEMERAL).forPath("/trans", "111".getBytes())
.and().setData().forPath("/curator", "222".getBytes())
.and().commit();
for (CuratorTransactionResult c:commit) {
System.out.println(c.getForPath()+"-->"+c.getType()+"-->"+c.getResultPath()+"-->"+c.getResultStat()+"-->"+c.getError());
}*/
/**
* 节点事件
* 三种watcher来做节点监听
* PathChildrenCache 监视一个路径下子节点的创建 删除 节点数据更新
* NodeCache 监视一个节点的创建 更新 删除
* TreeCache PathChildrenCache+NodeCache 监视路径下的创建 更新 删除 缓存路径下所有子节点的数据
*/
//NodeCache 第三个参数 是 是否压缩
/* NodeCache nodeCache = new NodeCache(curatorFramework, "/curator", false);
nodeCache.start(true);
nodeCache.getListenable().addListener(()-> System.out.println("节点数据变化后的结果->"+new String(nodeCache.getCurrentData().getData())));
curatorFramework.setData().forPath("/curator", "444".getBytes());*/
//PathChildrenCache 第三个参数是是否缓存
PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework, "/curator1", true);
pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
pathChildrenCache.getListenable().addListener((curatorFramework1,pathChildrenCacheEvent)->{
switch (pathChildrenCacheEvent.getType()){
case CHILD_ADDED:
System.out.println("增加子节点");
break;
case CHILD_UPDATED:
System.out.println("修改子节点");
break;
case CHILD_REMOVED:
System.out.println("删除子节点");
break;
default:break;
}
});
//创建节点
curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/curator1/node1","123".getBytes());
TimeUnit.SECONDS.sleep(2);
//修改子节点
curatorFramework.setData().forPath("/curator1/node1","456".getBytes());
TimeUnit.SECONDS.sleep(2);
//删除子节点
curatorFramework.delete().forPath("/curator1/node1");
//让线程等待
System.in.read();
}
}