应用配置集中到节点上,应用启动时主动获取,并在节点上注册一个 watcher,每次配置更新都会通知到应用。数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中心,顾名思义就是发布者将数据发布到 ZooKeeper 的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
~
本篇内容包括:Demo 概述、代码实现、测试结果
应用配置集中到节点上,应用启动时主动获取,并在节点上注册一个 watcher,每次配置更新都会通知到应用。
数据发布/订阅(Publish/Subscribe)系统,即所谓的配置中心,顾名思义就是发布者将数据发布到 ZooKeeper 的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
采用发布/订阅模式将配置信息发布到 Zookeeper 节点上,供订阅者动态获取数据:
参考:Mac通过Docker安装Zookeeper集群
org.apache.zookeeper zookeeper 3.7.0
import java.io.IOException;
import java.util.concurrent.CountDownLatch;import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;public class ConnectionWatcher implements Watcher {private final CountDownLatch connectedSignal = new CountDownLatch(1);private static final int SESSION_TIMEOUT = 5000;protected ZooKeeper zk;public void connect(String hosts) throws IOException, InterruptedException {zk = new ZooKeeper(hosts, SESSION_TIMEOUT, this);connectedSignal.await();}@Overridepublic void process(WatchedEvent event) {if (event.getState() == Event.KeeperState.SyncConnected) {connectedSignal.countDown();}}public void close() throws InterruptedException {zk.close();}}
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.Stat;public class ActiveKeyValueStore extends ConnectionWatcher {private static final Charset CHARSET = StandardCharsets.UTF_8;/*** 读取节点数据** @param path 节点地址* @param value 数据值* @throws InterruptedException 中断异常* @throws KeeperException ZooKeeper异常*/public void write(String path, String value) throws InterruptedException, KeeperException {Stat stat = zk.exists(path, false);if (stat == null) {if (value == null) {zk.create(path, null,ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);} else {zk.create(path, value.getBytes(CHARSET),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}} else {if (value == null) {zk.setData(path, null, -1);} else {zk.setData(path, value.getBytes(CHARSET), -1);}}}/*** 读取节点数据** @param path 节点地址* @param watcher watcher* @return 数据值* @throws InterruptedException 中断异常* @throws KeeperException ZooKeeper异常*/public String read(String path, Watcher watcher) throws InterruptedException, KeeperException {/* stat */byte[] data = zk.getData(path, watcher, null);return new String(data, CHARSET);}
}
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;import org.apache.zookeeper.KeeperException;public class ConfigUpdater {public static final String PATH = "/configuration";private final ActiveKeyValueStore store;private final Random random = new Random();public ConfigUpdater(String hosts) throws IOException, InterruptedException {//定义一个类store = new ActiveKeyValueStore();//连接Zookeeperstore.connect(hosts);}public void run() throws InterruptedException, KeeperException {// noinspection InfiniteLoopStatementwhile (true) {String value = random.nextInt(100) + "";//向 ZNode 写数据(也可以将xml文件写进去)store.write(PATH, value);System.out.printf("Set %s to %s\n", PATH, value);TimeUnit.SECONDS.sleep(random.nextInt(10));}}public static void main(String[] args) throws IOException, InterruptedException, KeeperException {String hosts = "localhost:2181";ConfigUpdater updater = new ConfigUpdater(hosts);updater.run();}
}
import java.io.IOException;import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;public class ConfigWatcher implements Watcher {private final ActiveKeyValueStore store;public ConfigWatcher(String hosts) throws InterruptedException, IOException {store = new ActiveKeyValueStore();//连接Zookeeperstore.connect(hosts);}public void displayConfig() throws InterruptedException, KeeperException {String value = store.read(ConfigUpdater.PATH, this);System.out.printf("Read %s as %s\n", ConfigUpdater.PATH, value);}@Overridepublic void process(WatchedEvent event) {System.out.printf("Process incoming event: %s\n", event.toString());if (event.getType() == Event.EventType.NodeDataChanged) {try {displayConfig();} catch (InterruptedException e) {System.err.println("Interrupted. Exiting");Thread.currentThread().interrupt();} catch (KeeperException e) {System.err.printf("KeeperException: %s. Exiting.\n", e);}}}public static void main(String[] args) throws IOException, InterruptedException, KeeperException {String hosts = "localhost:2181";//创建 watcherConfigWatcher watcher = new ConfigWatcher(hosts);//调用 display 方法watcher.displayConfig();//然后一直处于监控状态Thread.sleep(Long.MAX_VALUE);}
}
Set /configuration to 76
Set /configuration to 55
Set /configuration to 13
...
Read /configuration as 76
Read /configuration as 55
Read /configuration as 13
...
通过 ConfigUpdater 发布的信息以及 ConfigWatcher 监控得到的信息可以看出,已经成功模拟实现集群配置信息的订阅发布
下一篇:【C语言入门数据结构】顺序表