概述:理论暂略
一、windows 集群安装
1、复制3分zoo?.cfg的文件
每份文件分别是
1.1 zoo1.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=D:\\zookeeper-3.4.12\\data\\1 //持续递增
dataLogDir=D:\\zookeeper-3.4.12\\log\\1 //持续递增
clientPort=2181 //持续递增
server.1=127.0.0.1:2887:3887 //第一个端口此server和集群中 Leader服务器交换信息的端口,第二个端口是选举端口
server.2=127.0.0.1:2888:3888
server.3=127.0.0.1:2889:3889
1.2 zoo2.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=D:\\zookeeper-3.4.12\\data\\2
dataLogDir=D:\\zookeeper-3.4.12\\log\\2
clientPort=2182
server.1=127.0.0.1:2887:3887
server.2=127.0.0.1:2888:3888
server.3=127.0.0.1:2889:3889
1.3 zoo3.cfg 略!
2、需要复制3个启动脚本.并且指定启动脚本zkServer?.cmd内的增加一个参数 set ZOOCFG=../conf/zoo?.cfg
3、在data/?下创建一个文本文件myid (无后缀) 并且依次填上?的数值
4、启动即可!
二、基本操作APi
/**
一、基本操作APi
1、创建一个目录节点
zk.create("/testRootPath2", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
2、创建一个子目录节点
zk.create("/testRootPath2/testChildPathOne", "testChildDataOne".getBytes(),
Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
3、取节点数据
new String(zk.getData("/testRootPath2",false,null)) // false不监控
4、取子节点目录
zk.getChildren("/testRootPath2",true)
5、修改子节点数据
zk.setData("/testRootPath2/testChildPathOne","modifyChildDataOne".getBytes(),-1);
*/
public class ZookeeperDemo {
@Test
public void test1() throws Exception {
ZooKeeper zk = new ZooKeeper("localhost:"+2181,30000, new Watcher(){
// 监控所有被触发的事件
public void process(WatchedEvent event){
System.out.println(event.getPath()+"已经触发了" + event.getType() + "事件!");
}
});
//创建一个目录节点
zk.create("/testRootPath2", "testRootData".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
//创建一个子目录节点
zk.create("/testRootPath2/testChildPathOne", "testChildDataOne".getBytes(),
Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
//取出目录节点数据
System.out.println(new String(zk.getData("/testRootPath2",false,null)));
//取出子目录节点列表
System.out.println(zk.getChildren("/testRootPath2",true));
//修改子目录节点数据
zk.setData("/testRootPath2/testChildPathOne","modifyChildDataOne".getBytes(),-1);
System.out.println("目录节点状态:["+zk.exists("/testRootPath2",true)+"]");
// 创建另外一个子目录节点
zk.create("/testRootPath2/testChildPathTwo", "testChildDataTwo".getBytes(),Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
System.out.println(new String(zk.getData("/testRootPath2/testChildPathTwo",true,null)));
// 删除子目录节点
zk.delete("/testRootPath2/testChildPathTwo",-1);
zk.delete("/testRootPath2/testChildPathOne",-1);
// 删除父目录节点
zk.delete("/testRootPath2",-1);
Thread.sleep(1000);
// 关闭连接
zk.close();
}
}
三、Acl介绍
* 概述: ACL:Access Control List 访问控制列表 ACL
* 权限控制,使用:scheme:id:perm 来标识,
* 主要涵盖下面 3个方面 权限模式(Scheme):
* 授权的策略 --- newId('digest', ""); // 这个digest就是授权策略 授权对象(ID):
* 授权的对象 --- id的值就是授权对象; // 比如用户名密码,或者id策略的地址 权限(Permission):授予的权限 ---
* 模式和对象确定被授权的客户端,权限就是这个客户端可操作的读、写等操作。
*
* 特性: Zookeeper的权限全部是基于节点的,即一个节点有一个节点的权限,每个节点可以设置多种ACL, 子节点不能继承父节点的权限。
*
* Scheme模式:
world: 默认方式,相当于全部都能访问, 授权对象就是字符串anyone。
* auth: 认证方式,代表已经认证通过的用户。
* digest: 用户名密码的认证方式, new Id("digest", md5(username:password));。
* ip:地址授权方式,可采用id段方式,比如192.168.0.1/24。
public static void main(String[] args)
throws NoSuchAlgorithmException, IOException, KeeperException, InterruptedException {
// 设置节点acl_test全部客户端可以访问,但是只有密码正确可以写权限
List<ACL> acls = new ArrayList<>();
ACL acl1 = new ACL(Perms.READ, new Id("world", "anyone"));
Id id = new Id("digest", DigestAuthenticationProvider.generateDigest("admin:admin"));
ACL acl2 = new ACL(Perms.ALL, id);
acls.add(acl1);
acls.add(acl2);
ZooKeeper zk = new ZooKeeper("localhost:2181", 2000, null);
if (zk.exists("/acl_test", false) == null) {
zk.create("/acl_test", "acl_test_data".getBytes(), acls, CreateMode.PERSISTENT); // 此节点任意可读,认证可写
}
// 客户端设置权限对象,如果和节点创建时设置的权限认证通过,则可以操作。
zk.addAuthInfo("digest", "admin:adminx".getBytes());
List<ACL> acl = zk.getACL("/acl_test", new Stat());
System.out.println(acl.get(0).toString());
// zk.setData("/acl_test", "wefwef".getBytes(), -1);
// //出错,没有写权限KeeperErrorCode = NoAuth for /test3
zk.delete("/acl_test", -1); // 注意:ACL中的delete权限应该理解为对子节点的delete权限
}
四、Watch介绍
一句话攻略: 首先只有3个get类型的方法能够触发实现Watch方法的process方法。
* exists 触发 目标节点的创建,删除,数据更新
* getData 比较上面----目标节点除了创建。删除,和更新都可以触发
* getChildren 子节点的创建和删除会触发NodeChildrenChanged,以及父节点本身的删除
private ZooKeeper connection() throws IOException{
return new ZooKeeper("localhost:" + 2181, 10000, new Watcher() {
public void process(WatchedEvent event) {
if (event.getPath() == null) { // 连接时会触发None的事件
System.out.println("[初始化连接]" + event.getPath() + ":" + event.getType());
} else if (event.getType() == Event.EventType.NodeDeleted) {
System.out.println("[节点删除]" + event.getPath() + ":" + event.getType());
} else if (event.getType() == Event.EventType.NodeCreated) {
System.out.println("[节点创建]" + event.getPath() + ":" + event.getType());
} else if (event.getType() == Event.EventType.NodeDataChanged) {
System.out.println("[节点更新]" + event.getPath() + ":" + event.getType());
} else if (event.getType() == Event.EventType.NodeChildrenChanged) {
System.out.println("[子节点更新(父增删子节点)]" + event.getPath() + ":" + event.getType());
}
}
});
}
/**
* 测试exists事件: --> 被监视的Znode创建(create:NodeCreate)、删除(delete:NodeDelete)、更新(setData:NodeDataChange)时被触发。
*/
@Test
public void test1() throws IOException, KeeperException, InterruptedException{
ZooKeeper zk = connection();
Stat stat = zk.exists("/root", true);
if (stat == null) {
zk.create("/root", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
Stat stat2 = zk.exists("/root", true);
if (stat2 == null) {
zk.create("/root", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
} else {
zk.setData("/root", "haha".getBytes(), -1);// -1好像表示全部版本
}
// zk.delete("/root",-1); 此时不触发事件,因为一个get类型方法只能触发它对应的事件一次。
Stat stat3 = zk.exists("/root", true);
if (stat3 != null) {
zk.delete("/root", -1);
}
}
/**
* 测试getData事件: --> 被监视的Znode删除(delete:NodeDelete)、更新(setData:NodeDataChange)时被触发。
*/
@Test
public void test2() throws IOException, KeeperException, InterruptedException{
ZooKeeper zk = connection();
Stat stat = zk.exists("/root", false);
if(stat==null){
zk.create("/root", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
byte[] data1 = zk.getData("/root", true, null);
System.out.println(new String(data1));
zk.setData("/root", "haha".getBytes(), -1); //触发NodeDataChange
byte[] data2 = zk.getData("/root", true, null);
System.out.println(new String(data2));
zk.delete("/root",-1);
}
/**
* 测试getChildren事件:-->父节点下的子增删 (delete:NodeChildrenChanged) 父节点自己的删除 (delete:NodeDelete)
*/
@Test
public void test3() throws IOException, KeeperException, InterruptedException{
ZooKeeper zk = connection();
Stat stat = zk.exists("/root", false);
if(stat==null){
zk.create("/root", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
//---------------------------------------------------------
zk.getChildren("/root", true);
zk.create("/root/children1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
zk.getChildren("/root", true);
zk.create("/root/children2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
zk.getChildren("/root", true);
zk.delete("/root/children1", -1);
zk.delete("/root/children2", -1);
zk.getChildren("/root", true);
zk.delete("/root", -1); /**不能级联删除,要把子节点全删掉!*/
}
.五、ZkClient的使用
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
四、zkClient的使用
概述:IZKConnection:是一个ZkClient与Zookeeper之间的一个适配器;在代码里直接使用的是ZKClient,
实质上还是委托了zookeeper进行处理。
在ZKClient中,根据事件类型,分为 :
节点事件(数据事件),对应的事件处理器是IZKDataListener;
子节点事件,对应的事件处理器是IZKChildListener;
Session事件,对应的事件处理器是IZKStatusListener;
ZkEventThread:是专门用来处理事件的线程
启动ZKClient:在创建ZKClient对象时,就完成了到ZooKeeper服务器连接的建立
1)、启动时,制定好connection string,连接超时时间,序列化工具等
2)、创建并启动eventThread,用于接收事件,并调度事件监听器Listener的执行
3)、连接到Zookeeper服务器,同时将ZKClient自身作为默认的Watcher
*/
public class ClientDemo {
public static final String PATH_DELIMITER = "/";
public static ZkClient zkClient = ZkClientUtil.createZkClient("127.0.0.1:2171", null);
/**
* 1、基础api的演示
*/
public static void main(String[] args) {
String path = "/root";
zkClient.deleteRecursive(path); // 递归删除
zkClient.createPersistent(path, "root_data"); // 创建持久节点
zkClient.writeData(path, "root_data2"); // 写数据
User user = new User();
user.setId(1);
user.setUsername("zhangsan");
zkClient.writeData(path, user);
System.out.println(((User) zkClient.readData(path)).getUsername());
zkClient.createPersistent(path+"/child1", true); // 递归创建
zkClient.writeData(path+"/child1", "child1_data");
Object readData = zkClient.readData(path+"/child1");
System.out.println(readData);
List<String> list = zkClient.getChildren(path); // 得到子节点
for (String string : list) {
System.out.println(string);
}
}
/**
* 2、服务节点发现创建的演示
* 服务父节点采用持久节点, 服务子项采用临时节点,节点不存在就创建。
*/
@Test
public void test2() {
String[] uris = new String[] { "ip1", "ip2" };
String servicePath = "/server_root" + PATH_DELIMITER + "server_name" + "/service";
if (!zkClient.exists(servicePath)) {
// 没有该节点就创建
zkClient.createPersistent(servicePath, true);
}
for (String uri : uris) {
String uriPath = servicePath + PATH_DELIMITER + uri;
if (zkClient.exists(uriPath)) {
// 删除之前的节点
zkClient.delete(uriPath);
}
// 创建一个临时节点,会话失效即被清理
zkClient.createEphemeral(uriPath);
}
}
/**
* 3、监听器IZkDataListener的演示
*/
@Test
public void test3() {
String path = "/test_client_listenner";
zkClient.delete(path);
zkClient.subscribeDataChanges(path, new IZkDataListener() {
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
System.out.printf("数据更新%s,数据是:%s", dataPath, data);
System.out.println("");
}
@Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.printf("数据删除%s", dataPath);
}
});
zkClient.createPersistent(path, "data");
// 注释掉为正常监听使用
// zkClient.readData(path);
//
// zkClient.writeData(path, "data_new1");
//
// zkClient.readData(path);
//
// zkClient.writeData(path, "data_new2");
//
// zkClient.readData(path);
//
// zkClient.writeData(path, "data_new3");
//
// zkClient.readData(path);
//
// zkClient.writeData(path, "data_new4");
//
// zkClient.readData(path);
//
// zkClient.delete(path,-1);
// 下面演示无getData等操作的演示,
zkClient.writeData(path, "data_new1"); // 触发 -- 原因,每次Listener运行后,会再次注册watch。
zkClient.writeData(path, "data_new2");
zkClient.writeData(path, "data_new3"); // 触发 -- 同上
zkClient.writeData(path, "data_new4");
zkClient.delete(path,-1);
}
}
class User implements Serializable {
private static final long serialVersionUID = 7859434873243822876L;
private Integer id;
private String username;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}