<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.10version>
dependency>
log4j.properties
# 全局日志配置
log4j.rootLogger=INFO, stdout
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
@Before
的代码,会在执行真正的test程序之前执行,@after
则会在结束之后再执行。ZooKeeper
需要提供ip地址及端口号、超时时间,再使用了一个监视器。由于这是异步执行的开启客户端,因此做一个阻塞操作,防止还没开启就执行后面的操作,在真正打开了客户端之后,发送一个消息,并解掉阻塞。public class GetTest {
private final static String IP = "192.168.233.133:2181"; // ip及端口
private final static Integer TIMEOUT = 5000; // 超时时间,毫秒为单位
private final static CountDownLatch countDownLatch = new CountDownLatch(1);
private final static Logger log = Logger.getLogger(GetTest.class);
private ZooKeeper zooKeeper;
@Before
public void before() throws IOException, InterruptedException {
zooKeeper = new ZooKeeper(IP, TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
log.info("创建了连接");
countDownLatch.countDown();
}
});
countDownLatch.await(); //阻塞当前线程
}
@After
public void after() throws InterruptedException {
zooKeeper.close();
}
}
// 同步方式
create(String path, byte[] data, List<ACL> acl, CreateMode createMode)
// 异步方式
create(String path, byte[] data, List<ACL> acl, CreateMode createMode, AsyncCallback.StringCallback callBack,Object ctx)
path
: znode
路径。例如,/node1 /node1/node11
data
:要存储在指定 znode
路径中的数据acl
:要创建的节点的访问控制列表。zookeeper API提供了一个静态接口 ZooDefs.Ids
来获取一些基本的acl列表。例如,ZooDefs.Ids.OPEN_ACL_UNSAFE
返回打开znode
的acl
列表。createMode
:节点的类型,这是一个枚举。callBack
:异步回调接口ctx
:传递上下文参数public class ZooDefs {
public static final String[] opNames = new String[]{"notification", "create", "delete", "exists", "getData", "setData", "getACL", "setACL", "getChildren", "getChildren2", "getMaxChildren", "setMaxChildren", "ping"};
public ZooDefs() {
}
public interface Ids {
Id ANYONE_ID_UNSAFE = new Id("world", "anyone");
Id AUTH_IDS = new Id("auth", "");
ArrayList<ACL> OPEN_ACL_UNSAFE = new ArrayList(Collections.singletonList(new ACL(31, ANYONE_ID_UNSAFE)));
ArrayList<ACL> CREATOR_ALL_ACL = new ArrayList(Collections.singletonList(new ACL(31, AUTH_IDS)));
ArrayList<ACL> READ_ACL_UNSAFE = new ArrayList(Collections.singletonList(new ACL(1, ANYONE_ID_UNSAFE)));
}
public interface Perms {
int READ = 1;
int WRITE = 2;
int CREATE = 4;
int DELETE = 8;
int ADMIN = 16;
int ALL = 31;
}
public interface OpCode {
int notification = 0;
int create = 1;
int delete = 2;
int exists = 3;
int getData = 4;
int setData = 5;
int getACL = 6;
int setACL = 7;
int getChildren = 8;
int sync = 9;
int ping = 11;
int getChildren2 = 12;
int check = 13;
int multi = 14;
int auth = 100;
int setWatches = 101;
int sasl = 102;
int createSession = -10;
int closeSession = -11;
int error = -1;
}
}
ZooDefs.Ids.OPEN_ACL_UNSAFE
:表示开放权限,所有用户拥有所有权限ZooDefs.Ids.CREATOR_ALL_ACL
:表示使用 auth
权限模式,并且对于满足条件的用户开放所有权限ZooDefs.Ids.READ_ACL_UNSAFE
:表示对于所有用户,只开放Read权限ZooDefs.Ids.ANYONE_ID_UNSAFE
:是一个常用的Id对象,表示所有用户ZooDefs.Ids.AUTH_IDS
:是一个Auth模式的Id对象,具体操作还要在后面演示。ZooDefs.Perms
的权限参数组成。public enum CreateMode {
PERSISTENT(0, false, false),
PERSISTENT_SEQUENTIAL(2, false, true),
EPHEMERAL(1, true, false),
EPHEMERAL_SEQUENTIAL(3, true, true);
}
CreateMode.PERSISTENT
:表示持久无顺序的节点CreateMode.PERSISTENT_SEQUENTIAL
:表示持久有顺序的节点CreateMode.EPHEMERAL
:表示临时无顺序的节点CreateMode.EPHEMERAL_SEQUENTIAL
:表示临时有顺序的节点 @Test
public void testCreate1() throws KeeperException, InterruptedException {
// 创建了一个开放权限,持久的节点
// arg3:权限列表 world:anyone:cdrwa
// arg4:节点类型 持久化节点
zooKeeper.create("/hadoop/child", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
@Test
public void testCreate2() throws KeeperException, InterruptedException {
// Ids.READ_ACL_UNSAFE world:anyone:r
zooKeeper.create("/hadoop/child1", "data".getBytes(), ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT);
}
@Test
public void testCreate3() throws KeeperException, InterruptedException {
Id id = ZooDefs.Ids.ANYONE_ID_UNSAFE; // 所有用户
List<ACL> acl = Arrays.asList(new ACL(ZooDefs.Perms.READ, id), new ACL(ZooDefs.Perms.WRITE, id)); // 自己合成权限,读写权限wr
zooKeeper.create("/hadoop/child2", "data".getBytes(), acl, CreateMode.PERSISTENT);
}
@Test
public void testCreate4() throws KeeperException, InterruptedException {
Id ip = new Id("ip", "192.168.233.133"); // 设置ip权限
List<ACL> acl = Collections.singletonList(new ACL(ZooDefs.Perms.ALL, ip)); // 表示对指定ip开放全部权限
zooKeeper.create("/hadoop/child3", "data".getBytes(), acl, CreateMode.PERSISTENT);
}
@Test
public void testCreate5() throws KeeperException, InterruptedException {
zooKeeper.addAuthInfo("digest", "root:root".getBytes()); // 添加授权用户
// 设置为auth授权模式。使用内置的方式,即允许所有权限
// 设置为持久顺序节点,会将授权的用户添加进去
zooKeeper.create("/hadoop/child4", "data".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT_SEQUENTIAL);
}
值得一提的是,该方法需要调用
addAuthInfo
在zookeeper内部先注册一个用户。这里需要注意,不能忘了
@Test
public void testCreate6() throws KeeperException, InterruptedException {
// 自定义权限
// 授权模式和授权对象
zooKeeper.addAuthInfo("digest", "root:root".getBytes()); // 添加授权用户
Id id = new Id("auth", "root");
List<ACL> acl = Collections.singletonList(new ACL(ZooDefs.Perms.READ, id));
zooKeeper.create("/hadoop/child5", "data".getBytes(), acl, CreateMode.EPHEMERAL);// 在关闭当前客户端之前,其他客户端可以查得到该节点,关闭客户端之后这个节点就销毁了
Thread.sleep(10000);
}
@Test
public void testCreate7() throws KeeperException, InterruptedException {
// digest授权模式,需要提前计算密文
Id digest = new Id("digest", "root:qiTlqPLK7XM2ht3HMn02qRpkKIE=");
List<ACL> acl = Collections.singletonList(new ACL(ZooDefs.Perms.ALL, digest));
zooKeeper.create("/hadoop/child6", "data".getBytes(), acl, CreateMode.PERSISTENT);// 在关闭当前客户端之前,其他客户端可以查得到该节点,关闭客户端之后这个节点就销毁了
}
值得一提的是,需要提前计算好密文
@Test
public void testCreate8() throws InterruptedException {
// 创建了一个开放权限,持久的节点
// arg3:权限列表 world:anyone:cdrwa
// arg4:节点类型 持久化节点
zooKeeper.create("/hadoop/child7", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new AsyncCallback.StringCallback() {
@Override
public void processResult(int rc, String path, Object ctx, String name) {
log.info(rc); // 0代表成功了
log.info(path); // 传进来的,添加的节点
log.info(name); // 真正查到的节点的名字
log.info(ctx.toString()); // 上下文参数,ctx传进来的东西
log.info("终于结束啦!");
countDownLatch.countDown(); // 关闭等待
}
}, "ctx");
countDownLatch.await(); // 拥塞,等待结束
}
// 同步方式
setData(String path, byte[] data, int version)
// 异步方式
setData(String path, byte[] data, int version,AsyncCallback.StatCallback callBack, Object ctx)
path
: znode
路径。例如,/node1 /node1/node11
data
:要存储在指定 znode
路径中的数据version
:znode的当前版本。值为-1
时,表示不需要考虑版本。如果指定版本之后,就可以做成一个乐观锁。callBack
:异步回调接口ctx
:传递上下文参数 @Test
public void testSet1() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.setData("/hadoop", "newData".getBytes(), -1); // 返回状态信息
log.info(stat.toString()); // 将状态信息打印
log.info("当前版本号" + stat.getVersion());
log.info("节点创建时间" + stat.getCtime());
log.info("节点修改时间" + stat.getMtime());
}
@Test
public void testSet2() throws InterruptedException {
zooKeeper.setData("/hadoop", "newData".getBytes(), 4, new AsyncCallback.StatCallback() { // 乐观锁
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
log.info(rc); // 0 代表修改成功
log.info(path); // 输入的节点路径
log.info(stat.getVersion()); // 当前版本
countDownLatch.countDown(); // 解放
}
}, null); // 返回状态信息
countDownLatch.await(); // 阻塞
}
@Test
public void testSet3() throws KeeperException, InterruptedException {
zooKeeper.addAuthInfo("digest", "root:root".getBytes()); // 添加授权用户
Stat stat = zooKeeper.setData("/hadoop/child4", "newData".getBytes(), -1); // 对于有权限限制的节点,需要提前添加授权用户
log.info(stat.toString()); // 将状态信息打印
log.info("当前版本号" + stat.getVersion());
log.info("节点创建时间" + stat.getCtime());
log.info("节点修改时间" + stat.getMtime());
}
更新该节点的时候,需要注意,要先添加用户,否则没有权限更新
// 同步方式
delete(String path, int version)
// 异步方式
delete(String path, int version, AsyncCallback.VoidCallback callBack, Object ctx)
path
: znode
路径。例如,/node1 /node1/node11
version
:znode的当前版本。值为-1
时,表示不需要考虑版本。如果指定版本之后,就可以做成一个乐观锁。callBack
:异步回调接口ctx
:传递上下文参数 @Test
public void testDelete1() throws KeeperException, InterruptedException {
zooKeeper.delete("/hadoop/child1", -1); // 如果节点不存在,会删除失败
}
@Test
public void testDelete2() throws KeeperException, InterruptedException {
zooKeeper.delete("/hadoop/child40000000004", -1, new AsyncCallback.VoidCallback() {
@Override
public void processResult(int rc, String path, Object ctx) {
log.info(rc);
log.info(path);
}
}, null); // 如果节点不存在,会删除失败
}
// 同步方式
getData(String path, boolean b, Stat stat)
// 异步方式
getData(String path, boolean b,AsyncCallback.DataCallback callBack, Object ctx)
path
: znode
路径。例如,/node1 /node1/node11
b
:是否使用连接对象中注册的监视器stat
:返回znode的元数据callBack
:异步回调接口ctx
:传递上下文参数 @Test
public void testGet1() throws KeeperException, InterruptedException {
Stat stat = new Stat();
byte[] data = zooKeeper.getData("/hadoop", false, stat);
log.info("获取到的数据是:" + new String(data));
log.info("当前节点的版本:" + stat.getVersion());
}
@Test
public void testGet2() throws InterruptedException {
zooKeeper.getData("/hadoop", null, new AsyncCallback.DataCallback() {
@Override
public void processResult(int rc, String path, Object ctx, byte[] bytes, Stat stat) {
log.info(rc);
log.info(path);
log.info(new String(bytes));
log.info(stat.getVersion());
countDownLatch.countDown();
}
}, null);
countDownLatch.await(); // 拥塞
}
// 同步方式
getChildren(String path, boolean b)
// 异步方式
getChildren(String path, boolean b,AsyncCallback.ChildrenCallback callBack,Object ctx)
path
: znode
路径。例如,/node1 /node1/node11
b
:是否使用连接对象中注册的监视器callBack
:异步回调接口ctx
:传递上下文参数 @Test
public void testChildren() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren("/hadoop", null); // 此操作,类似于ls
children.forEach(System.out::println); // 获取所有子节点的名称
}
@Test
public void testChildren2() throws InterruptedException {
zooKeeper.getChildren("/hadoop", null, new AsyncCallback.Children2Callback() { // 此操作类似于ls2
@Override
public void processResult(int rc, String path, Object ctx, List<String> list, Stat stat) {
log.info(rc);
log.info(path);
log.info(stat.getVersion());
log.info(list.toString());
countDownLatch.countDown();
}
}, null);
countDownLatch.await();
}
// 同步方法
exists(String path, boolean b)
// 异步方法
exists(String path, boolean b,AsyncCallback.StatCallback callBack,Object ctx)
path
: znode
路径。例如,/node1 /node1/node11
b
:是否使用连接对象中注册的监视器callBack
:异步回调接口ctx
:传递上下文参数 @Test
public void existsTest() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists("/hadoop", null); // 这操作,相当于stat
log.info(stat.toString());
}
@Test
public void existsTest2() throws InterruptedException {
zooKeeper.exists("/hadoop", null, new AsyncCallback.StatCallback() {
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
log.info(rc);
log.info(path);
log.info(stat.getVersion());
log.info(stat.getCtime());
countDownLatch.countDown();
}
}, null);
countDownLatch.await();
}
Zookeeper
服务端Zookeeper
客户端ZKWatchManager
对象EventType
是数据节点(znode)发生变化时对应的通知类型。EventType
变化时 KeeperState
永远处于SyncConnected
通知状态下;当KeeperState
发生变化时, EventType
永远为None
。get
和exists
类方法中,可以添加Watcher
。getData()
方法的监听器可以监测节点的修改和删除;exists()
的监听器可以监测节点的修改和删除外,还可以检测节点的创建;getChildren()
的监听器可以监听该节点的子节点的修改与删除。KeeperState
的状态,而EventType
会保持为None
。Watcher
中的方法。并可以重复工作(究竟为什么不是一次性的,这个还需要考察)。EventType
是不是None
(一般都是)。然后根据KeepState
的状态,输出不同的日志。其中,当是连接成功的状态时,因为有可能外面的方法还在拥塞,因此顺便唤醒一下。public class ZKConnectionWatcher implements Watcher {
private CountDownLatch countDownLatch = null;
private static final Logger log = Logger.getLogger(ZKConnectionWatcher.class);
public ZKConnectionWatcher() {
}
public ZKConnectionWatcher(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getType() == Event.EventType.None) { // 说明没有发生除了连接问题以外的事件
switch (watchedEvent.getState()) {
case SyncConnected:
log.info("客户端与服务器正常连接");
if (this.countDownLatch != null) this.countDownLatch.countDown();
break;
case Disconnected:
log.info("客户端与服务器断开连接");
break;
case Expired:
log.info("会话SESSION超时");
break;
case AuthFailed:
log.info("身份认证失败");
break;
}
}
}
public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
}
Watcher
即可。最后加一个睡眠,可以有更多的测试。比如说,如果直接关闭zookeeper的服务器,就会触发方法,状态为Disconnected
。如果addAuthInfo
时digest
写错,状态就会变成AuthFailed
public class ConnectWatcherTest {
private final static String IP = "192.168.233.133:2181"; // ip及端口
private final static Integer TIMEOUT = 5000; // 超时时间,毫秒为单位
private final static CountDownLatch countDownLatch = new CountDownLatch(1);
private final static Logger log = Logger.getLogger(ConnectWatcherTest.class);
private ZooKeeper zooKeeper;
@Test
public void testWatcher() throws IOException, InterruptedException, KeeperException {
zooKeeper = new ZooKeeper(IP, TIMEOUT, new ZKConnectionWatcher(countDownLatch));
countDownLatch.await();
log.info("当前会话ID" + zooKeeper.getSessionId());
zooKeeper.addAuthInfo("digest", "root:root".getBytes());
Stat stat = new Stat();
byte[] data = zooKeeper.getData("/hadoop/child3", false, stat);
log.info("接收到的数据是:" + new String(data));
Thread.sleep(5000);
zooKeeper.close();
log.info("结束");
}
}
值得一提的是,监听器可以重复触发。只要有变化,都会触发。
// 使用连接对象的监视器
exists(String path, boolean b)
// 自定义监视器
exists(String path, Watcher w)
// NodeCreated:节点创建
// NodeDeleted:节点删除
// NodeDataChanged:节点内容发生变化
public class ZKCommonWatcher implements Watcher {
private CountDownLatch countDownLatch = null;
private static final Logger log = Logger.getLogger(ZKConnectionWatcher.class);
public ZKCommonWatcher() {
}
public ZKCommonWatcher(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void process(WatchedEvent watchedEvent) {
switch (watchedEvent.getType()) {
case NodeCreated:
log.info("节点创建了");
break;
case NodeDataChanged:
log.info("节点数据被修改了");
break;
case NodeDeleted:
log.info("节点被删除了");
break;
case None: {
Event.KeeperState state = watchedEvent.getState();
log.info(state);
if (this.countDownLatch != null && state == Event.KeeperState.SyncConnected)
this.countDownLatch.countDown();
break;
}
}
}
public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
}
public class ExistsWatcherTest {
private final static String IP = "192.168.233.133:2181"; // ip及端口
private final static Integer TIMEOUT = 5000; // 超时时间,毫秒为单位
private final static CountDownLatch countDownLatch = new CountDownLatch(1);
private final static Logger log = Logger.getLogger(ConnectWatcherTest.class);
private ZooKeeper zooKeeper;
@Before
public void before() throws IOException, InterruptedException {
zooKeeper = new ZooKeeper(IP, TIMEOUT, new ZKCommonWatcher(countDownLatch)); // 实例化一个watcher
countDownLatch.await(); //阻塞当前线程
}
@Test
public void testExists() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists("/hadoop/child100", true); // 使用在创建客户端时的watcher
if (stat != null) log.info(stat.getVersion());
Thread.sleep(100000);
}
@After
public void after() throws InterruptedException {
zooKeeper.close();
}
}
在注册Exists监视器的时候,选择true,使用默认的连接对象的监视器。连接方面的监视可以多次触发,但是节点状态方面的触发只能有一次。
exists
中注册一个匿名的Watcher
实现类,在方法中辨别事件类型,并根据不同的类型做出不同的处理。Watcher
只能触发一次,触发完之后就不能再监测了。 @Test
public void testExists2() throws KeeperException, InterruptedException {
// 一次性的,用完就没了
Stat stat = zooKeeper.exists("/hadoop/child100", new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
switch (watchedEvent.getType()) {
case NodeCreated:
log.info("x节点创建了");
break;
case NodeDataChanged:
log.info("x节点数据被修改了");
break;
case NodeDeleted:
log.info("x节点被删除了");
break;
}
}
});
if (stat != null) log.info(stat.getVersion());
Thread.sleep(100000);
}
@Test
public void testExists3() throws KeeperException, InterruptedException {
// 注册多个监听器
zooKeeper.exists("/hadoop/child100", new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
log.info("1");
log.info(watchedEvent.getType());
}
});
zooKeeper.exists("/hadoop/child100", new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
log.info("2");
log.info(watchedEvent.getType());
}
});
Thread.sleep(100000);
}
@Test
public void testExists4() throws KeeperException, InterruptedException {
String path = "/hadoop/child100";
// 重复使用,用完再注册一个新的
Stat stat = zooKeeper.exists(path, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
switch (watchedEvent.getType()) {
case NodeCreated:
log.info("x节点创建了");
break;
case NodeDataChanged:
log.info("x节点数据被修改了");
break;
case NodeDeleted:
log.info("x节点被删除了");
break;
}
try {
zooKeeper.exists(path, this); // 走之前,又注册一遍
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
});
if (stat != null) log.info(stat.getVersion());
Thread.sleep(100000);
}
getData
方法注册的监视器与上述方法类似,在此就不再赘述。// 使用连接对象的监视器
getChildren(String path, boolean b)
// 自定义监视器
getChildren(String path, Watcher w)
// NodeChildrenChanged:子节点发生变化
// NodeDeleted:节点删除
NodeChildrenChanged
@Test
public void testChildrenWatcher() throws KeeperException, InterruptedException {
String path = "/hadoop";
List<String> children = zooKeeper.getChildren(path, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
log.info("目前的状态是:" + watchedEvent.getState());
log.info("发生了" + watchedEvent.getType() + "事件");
try {
zooKeeper.getChildren(path, this); // 重新注册
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
});
Thread.sleep(100000);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class DBConfig {
private String ip;
private String username;
private String password;
private static final Logger log = Logger.getLogger(DBConfig.class);
public DBConfig(Map<String, String> config) throws NoSuchFieldException, IllegalAccessException {
importConfig(config);
log.info("创建DBConnection成功: " + this.toString());
}
public void importConfig(Map<String, String> config) throws NoSuchFieldException, IllegalAccessException {
for (Map.Entry<String, String> entry : config.entrySet()) {
String key = entry.getKey();
key = key.substring(key.lastIndexOf("/") + 1);
String value = entry.getValue();
Field field = this.getClass().getDeclaredField(key);
field.setAccessible(true);
field.set(this, value);
}
}
}
public class ConfigCenter implements Watcher, Closeable {
private String ip = null; // ip及端口
private static Integer timeOut = 5000; // 超时时间,毫秒为单位
private final static CountDownLatch countDownLatch = new CountDownLatch(1);
private final static Logger log = Logger.getLogger(ConfigCenter.class);
private ZooKeeper zooKeeper = null;
private DBConfig dbConfig = null;
private final Map<String, String> config = new HashMap<>();
public ConfigCenter() {
}
public ConfigCenter(String ip) throws IOException, InterruptedException {
this(ip, timeOut);
}
public ConfigCenter(String ip, Integer timeOut) throws IOException, InterruptedException {
this.ip = ip;
ConfigCenter.timeOut = timeOut;
initZK(ip, timeOut);
}
/**
* 初始化zookeeper
* @param ip
* @param timeOut
* @throws IOException
* @throws InterruptedException
*/
private void initZK(String ip, Integer timeOut) throws IOException, InterruptedException {
zooKeeper = new ZooKeeper(ip, timeOut, this);
countDownLatch.await();
}
/**
* 编辑config的键值对,修改或增加某个键值对
*
* @param path
* @param data
* @return
*/
private Map<String, String> editConfig(String path, byte[] data) {
config.put(path, new String(data));
return config;
}
/**
* 初始化config键值对,在zookeeper中读出来
*
* @throws KeeperException
* @throws InterruptedException
*/
private void initConfig() throws KeeperException, InterruptedException {
byte[] ip = zooKeeper.getData("/config/ip", true, null); // 每次触发之后,会重新初始化监听器
editConfig("/config/ip", ip);
byte[] username = zooKeeper.getData("/config/username", true, null);
editConfig("/config/username", username);
byte[] password = zooKeeper.getData("/config/password", true, null);
editConfig("/config/password", password);
}
/**
* 读取zookeeper的配置值,并且放入到DBConnection中
*/
private void initDB() {
try {
initConfig();
if (dbConfig == null) {
dbConfig = new DBConfig(config);
} else {
dbConfig.importConfig(config);
}
log.info("初始化DBConnection成功!");
} catch (KeeperException | InterruptedException | NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public void process(WatchedEvent watchedEvent) {
// 如果是None 说明是连接方面的变化
if (watchedEvent.getType() == Event.EventType.None) {
switch (watchedEvent.getState()) {
case SyncConnected:
log.info("连接 " + ip + "成功");
initDB(); // 初始化db
countDownLatch.countDown();
break;
case Disconnected:
log.error("连接 " + ip + "已断开");
break;
case Expired:
log.error("连接 " + ip + "已超时,需要重新连接服务器端");
try { // 重新连接
this.initZK(ip, timeOut);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
break;
case AuthFailed:
log.error("身份验证失败");
break;
}
} else { // 如果不是连接方面的变化,那就是监视的节点发生了变化
switch (watchedEvent.getType()) {
case NodeDataChanged:
log.info("配置的节点信息发生了变化,赶紧重新配置!");
initDB(); // 因为配置发生了变化,因此需要重新初始化db
break;
case NodeCreated:
log.info("添加了配置信息,目前的配置信息是");
break;
}
}
}
@Override
public void close() throws IOException {
try {
zooKeeper.close();
log.info("zooKeeper已关闭");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}