编写不易,转载请注明(http://shihlei.iteye.com/blog/2075801)!
一概述
Zookeeper是针对大型分布式系统的可靠协调系统。
核心服务总结如下:
- 可靠的协调系统:用于存储客户端集群相互协作的信息。(Zookeeper核心机制会保证数据在所有的Zookeeper数据结点的一致性,客户并发修改任何Zookeeper结点的数据效果都是一致的)。
- 数据变更通知推送:当客户端修改某个数据时,例如:变更,增加,删除(对于瞬时ZNode,客户端失联自动删除ZNode),Zookeeper主动通知监听该数据变化的客户端。
二 数据存储
1)Zookeeper 中存储数据的结点为ZNode 有唯一路径,并有类似于树形的结构
2)ZNode有如下4种类型:
- PERSISTENT:持久性ZNode,不会随着client session的close/expire而消失。
- PERSISTENT_SEQUENTIAL:有顺序的持久性ZNode。
- EPHEMERAL:暂时行ZNode,生命周期依赖于client session,对应session close/expire后其znode也会消失。
- EPHEMERAL_SEQUENTIAL:有顺序的暂时行ZNode。
操作ZNode样例:
public void run(ZooKeeper zk) throws KeeperException, InterruptedException {
String nodePath = "/TestNode";
byte[] nodeDate = "TestNodeData".getBytes();
// 1 创建znode ,CreateMode.EPHEMERAL 为临时的,客户端断开则无法看到znode信息
// 创建一个znode(必须有父节点)
String result = zk.create(nodePath, nodeDate, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println("Create znode at : " + result);
// 2 测试一个znode是否存在,并且查询它的元数据,null表示不存在
Stat stat = zk.exists(nodePath, false);
System.out.println("Create Exist znode : " + stat);
// 3 获取一个znode的ACL,每个znode被创建时都会带有一个ACL列表,用于决定谁可以对它执行何种操作。
System.out.println("ACL List ===============");
List<ACL> aclList = zk.getACL(nodePath, stat);
for (ACL a : aclList) {
System.out.println(a);
}
// 4 获取一个znode的子节点列表
List<String> childs = zk.getChildren(nodePath, false);
for (String c : childs) {
System.out.println(c);
}
// 5.1 设置一个znode所保存的数据
nodeDate = zk.getData(nodePath, false, stat);
System.out.println("NodeDate : " + new String(nodeDate));
// 5.2 设置一个znode所保存的数据
byte[] newNodeDate = "TestNodeData_NewData".getBytes();
// 注:Zookeeper中的更新操作是有条件的。在使用delete或者setData操作时必须提供被更新znode的版本号,如果版本号不匹配,则更新操作失败。
zk.setData(nodePath, newNodeDate, stat.getVersion());
}
三 应用场景
- 感知集群变化:Client启动在统一目录下时注册EPHEMERAL ZNode,当一台Client当机,其他znode可以收到通知,添加机器同。
- Master选举:对等机之间选取Master,Master失效后自动重选。实现:Client 创建EPHEMERAL_SEQUENTIAL ZNode,启动时,Sequental 号最小的当选为Master。
- 集群同步锁:Client 集群间实现同步操作。实现:Client 执行同步操作前创建 EPHEMERAL_SEQUENTIAL ZNode,如果ZNode Sequental 号最小则获得锁执行,否则等待其他Client释放锁。
同步锁实现:
package x.bd.zookeeper.lock;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import org.apache.zookeeper.CreateMode;
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;
/**
* 共享锁:原理,当客户端要进行某种操作时,在Zookeeper上注册Znode,如果当前序号为自己,则进行处理,处理完成后释放,其他等待处理的线程监控
*
* 这里用不同的Thread 模拟不同的Server
*
* 注:这种锁的概念同样可以应用与Master操作,其他同步的Salver等待
*
* 存在问题:判断锁的时候需要获取最小的锁,排序获得
*
* @author shilei
*
*/
public class LockTest implements Watcher {
private ZooKeeper zk;
// Zookeeper 锁根
private static final String LOCK_ROOT = "/Lock";
// 当前客户端注册锁名
private String lockName;
// 线程锁,用于没有获得执行锁时阻塞
private Integer threadLock = -1;
public LockTest(String server) throws Exception {
zk = new ZooKeeper(server, 3000, this);
}
/**
* 注册锁
*
* @throws Exception
*/
public void lock() throws Exception {
// 注册锁根结点
Stat rootStat = zk.exists(LOCK_ROOT, false);
if (rootStat == null) {
String rootPath = zk.create(LOCK_ROOT, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("Create root : " + rootPath);
}
// 在服务器注册自己的锁
lockName = zk.create(LOCK_ROOT + "/lock_", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("Create lock : " + lockName);
// 循环判断服务器当前锁是否为自己的锁
while (true) {
// 设置观察
List<String> lockList = zk.getChildren(LOCK_ROOT, true);
Collections.sort(lockList);
if (!lockName.endsWith(lockList.get(0))) {
// 当前锁非本服务器注册,等待
synchronized (threadLock) {
threadLock.wait();
}
} else {
// 获得锁成功
return;
}
}
}
/**
* 释放锁
*
* @throws Exception
*/
public void unLock() throws Exception {
Stat stat = zk.exists(lockName, false);
if (stat != null) {
zk.delete(lockName, stat.getVersion());
}
}
/**
* 竞争锁
*/
public void process(WatchedEvent event) {
// 事件发生在锁目录上
String path = event.getPath();
if (path != null && path.startsWith(LOCK_ROOT)) {
// 监控的是root node 需要判断node children changed 事件
if (event.getType() == Event.EventType.NodeChildrenChanged) {
// 事件类型为锁删除事件,激活锁判断
synchronized (threadLock) {
threadLock.notify();
}
}
}
}
public void shutdown() throws InterruptedException {
zk.close();
}
/**
* 期望结果:线程处理过程没有多个线程的嵌套流程
*/
public static void main(String[] args) throws Exception {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
public void run() {
try {
String server = "8.8.8.13:2181";
LockTest lockTest = new LockTest(server);
// 注册锁
lockTest.lock();
// 执行获取锁后的任务
System.out.println("Thread : " + Thread.currentThread().getId() + " start ! ");
Thread.sleep(1000 + new Random().nextInt(5000));
System.out.println("Thread : " + Thread.currentThread().getId() + " finish ! ");
// 获取锁后的任务执行完毕,释放
lockTest.unLock();
lockTest.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
}