ZooKeeper构建应用之配置服务器

配置信息的集中管理与共享是分布式应用的基本需求。对于此类需求,ZooKeeper可以提供两个层面的特性。一个是提供高可用存储服务,允许分布式应用的参与者更新、读取ZooKeeper中的配置信息。另一个是主动配置服务,当ZooKeeper中的配置信息发生变更时,主动通知设置了监视器的客户端,客户端收到通知后,立刻执行相应操作。

简化起见,本示例有两个前置条件。
第一,配置项的名称直接使用znode的路径表示,而znode中存储的字符串值就是配置项的值。
第二,在任何时间,只有单个客户端对配置执行更新操作。

对ZooKeeper执行读写逻辑封装在ActiveKeyValues中,代码如下:

public class ActiveKeyValueStore extends ConnectionWatcher {
    private static final charset CHARSET = charset.forName("UTF-8");

    public void write(String path, String value) throws InterruptedException,
        KeeperException {
      Stat stat = zk.exists(path, false);
      if (stat == null) {
        zk.create(path, value.getBytes(CHARSET), Ids.OPEN_ACL_UNSAFE,
            CreateMode.PERSISTENT);
       } else {
          zk.setData(path, value.getBytes(CHARSET), -1);
       }
    }

    public String read(String path, Watcher watcher) throws InteruptedException,
        KeeperException {
      byte[] data = zk.getData(path, watcher, null/*stat*/);
      return new String(data, CHARSET);
    }
}

以上代码中,write方法负责写操作,如果znode已经存在则直接设置其值,如果不存在先创建znode再设置其值。写操作返回znode中保存的值,最重要的是read方法中的Wacher参数,读客户端需要注册此监视器以便接收通知。ConnectionWatcher基类负责创建ZooKeeper实例、用户身份认证及与ZooKeeper服务器的连接。

ConfigUpdater类按随机时间间隔更新ZooKeeper中的配置项,充当更新配置客户端,代码如下:

public class ConfigUpdate {
    public static final String PATH = "/config";

    private ActiveKeyValueStore store;
    private Random random = new Random();

    public ConfigUpdater(String hosts) throws IOException, InterruptedException {
        store = new ActiveKeyValueStore();
        store.connect(hosts);
    }

    public void run throws InterruptedException, KeeperException {
        while (ture) {
          String value = random.nextInt(100) + '';
          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 Exception {
        ConfigUpdater configUpdater = new ConfigUpdater(args[0]);
        configUpdater.run();
    }
}

ConfigWatcher作为配置服务的读客户端,代码如下:

public class ConfigWatcher implements Watcher {

    private ActiveKeyValueStore store;

    public configWatcher(String hosts) throws IOException, InterreptedException {
        store = new ActiveKeyValueStore();
        store.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);
    }

    @Override
    public void process(WatcherEvent event) {
        if (event.getType() == EventType.NodeDataChanged) {
            try {
                displayConfig();
            } catch (InterruptedException e) {
                System.err.println("Interrupted. Exiting.");
                Thread.currentThread().interrupted();
            } catch (KeeperException e) {
                System.err.printf("KeeperException: %s. Exiting.\n", e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        ConfigWatcher configWatcher = new ConfigWatcher(args[0]);
        configWatcher.displayConfig();

        Thread.sleep(Long.MAX_VALUE);
    }
}

代码中的主要逻辑在process中实现,当配置发生变更时,客户端收到通知后显示新的配置内容。

你可能感兴趣的:(Hadoop)