主要的理论内容在这里:
http://blog.csdn.net/xinguan1267/article/details/38422149
关键有几个知识点:
1、session:client与server之间的连接,超时
2、znode:zookeeper的文件储存格式(inode23333),数据大小(1M),权限控制(ACL),模式:(持久或者session消失就没了),version,cversion,czxid,mzxid,pzxid
3、基本架构:一个leader,多个follower,Observer
4、读写过程:写过程:2次网络IO,一次硬盘IO,读过程:不需要经过leader
5、特性:写log,线性化,FIFO
6、api说明:
接下来是一个应用zookeeper的小demo
/**
* Created by wangjian36 on 17/1/19.
*/
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;
/**
*
* @author http://blog.csdn.net/java2000_wl
* @version <b>1.0</b>
*/
public class ZookeeperTest {
private static final int SESSION_TIMEOUT = 30000;
private Watcher watcher = new Watcher() {
public void process(WatchedEvent event) {
System.out.println("process : " + event.getType());
if(Event.KeeperState.SyncConnected == event.getState()){
connectedSemaphore.countDown();
}
}
};
private ZooKeeper zooKeeper;
private CountDownLatch connectedSemaphore = new CountDownLatch(1);
/**
* 连接zookeeper
* <br>------------------------------<br>
* @throws IOException
*/
public void connect() throws IOException, InterruptedException {
zooKeeper = new ZooKeeper("localhost:2181", SESSION_TIMEOUT, watcher);
connectedSemaphore.await();
}
Semaphore semaphore = new Semaphore(1);
public void connectWithLock() throws IOException, InterruptedException {
semaphore.acquire();
zooKeeper = new ZooKeeper("localhost:2181", SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("connection started");
semaphore.release();
}
});
semaphore.acquire();
}
/**
* 关闭连接
* <br>------------------------------<br>
*/
public void close() {
try {
zooKeeper.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 创建一个znode
* 1.CreateMode 取值
* PERSISTENT:持久化,这个目录节点存储的数据不会丢失
* PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目录节点会根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名;
* EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是 session过期超时,这种节点会被自动删除
* EPHEMERAL_SEQUENTIAL:临时自动编号节点
* <br>------------------------------<br>
*/
public void testCreate(String path,String data) throws KeeperException, InterruptedException {
String result = null;
result = zooKeeper.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode
.PERSISTENT);
System.out.println("create result : {}"+result);
}
/**
* 删除节点 忽略版本
* <br>------------------------------<br>
*/
public void testDelete(String path) {
try {
zooKeeper.delete(path, -1);
} catch (Exception e) {
}
}
/**
* 获取数据
* <br>------------------------------<br>
*/
public void testGetData(String path) throws KeeperException, InterruptedException {
String result = null;
Stat stat = new Stat();
byte[] bytes = zooKeeper.getData(path, null, stat);
result = new String(bytes);
System.out.println("getdata result : "+result+"\nstat_children "+stat.getNumChildren());
}
/**
* 获取数据 设置watch
* <br>------------------------------<br>
*/
public void testGetDataWatch(String path) {
String result = null;
try {
byte[] bytes = zooKeeper.getData(path, new Watcher() {
public void process(WatchedEvent event) {
System.out.println("testGetDataWatch watch : {}"+event.getType());
}
}, null);
result = new String(bytes);
} catch (Exception e) {
}
System.out.println("getdata result : {}"+result);
}
/**
* 判断节点是否存在
* 设置是否监控这个目录节点,这里的 watcher 是在创建 ZooKeeper实例时指定的 watcher
* <br>------------------------------<br>
*/
public Stat testExists(String path) {
Stat stat = null;
try {
stat = zooKeeper.exists(path, false);
} catch (Exception e) {
}
System.out.println("exists result : {}"+stat.getCzxid());
return stat;
}
/**
* 设置对应znode下的数据 , -1表示匹配所有版本
* <br>------------------------------<br>
*/
public void testSetData(String path) {
Stat stat = null;
try {
stat = zooKeeper.setData(path, "testSetData".getBytes(), -1);
} catch (Exception e) {
}
System.out.println("exists result : {}"+ stat.getVersion());
}
/**
* 判断节点是否存在,
* 设置是否监控这个目录节点,这里的 watcher 是在创建 ZooKeeper实例时指定的 watcher
* <br>------------------------------<br>
*/
public void testExistsWatch1(String path) {
Stat stat = null;
try {
stat = zooKeeper.exists(path, true);
} catch (Exception e) {
}
try {
zooKeeper.delete(path, -1);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 判断节点是否存在,
* 设置监控这个目录节点的 Watcher
* <br>------------------------------<br>
*/
public void testExistsWatch2(String path) {
Stat stat = null;
try {
stat = zooKeeper.exists(path, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("testExistsWatch2 watch : {}"+event.getType());
}
});
} catch (Exception e) {
}
// 触发watch 中的process方法 NodeDataChanged
try {
zooKeeper.setData("/zk002", "testExistsWatch2".getBytes(), -1);
} catch (Exception e) {
e.printStackTrace();
}
// 不会触发watch 只会触发一次
try {
zooKeeper.delete("/zk002", -1);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取指定节点下的子节点
* <br>------------------------------<br>
*/
public void testGetChild() {
try {
zooKeeper.create("/zk/001", "001".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zooKeeper.create("/zk/002", "002".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
List<String> list = zooKeeper.getChildren("/zk", true);
for (String node : list) {
System.out.println("node {}"+node);
}
} catch (Exception e) {
}
}
public static void main(String args[]) throws IOException, KeeperException, InterruptedException {
ZookeeperTest zookeeperTest = new ZookeeperTest();
zookeeperTest.connectWithLock();
//zookeeperTest.testCreate("/java_test/test","Hello_Zookeeper");
Stat stat = zookeeperTest.testExists("/java_test/test");
if(stat!=null)
zookeeperTest.testGetData("/java_test");
}
}
之所以必须要加这块东西,主要是因为链接服务器是一个比较慢的过程,如果还没链接到服务器上,结果就要对文件进行操作,那就会导致error,因此必须将链接服务器的函数,在服务器确定能够链接上,返回一个watcher信息的时候,才唤醒链接函数,这样才能保证当connect函数结束的时候,一定链接上了服务器。
类似的同步方法应该都可以的。
我自己拿信号量实现了一个,不过很不优雅。