Zookeeper高级应用之分布式配置管理

1. 需求概述

程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。现在把这些配置全部放到ZooKeeper上去,保存在 ZooKeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 ZooKeeper 的通知,然后从ZooKeeper 获取新的配置信息应用到系统中就好。

客户端发送消息到zk一次即可!

集群中的所有的服务器,都监听。都能立即受到关于这个消息的通知

2. 实现

package de.apps.config;

import org.apache.zookeeper.*;

import java.util.List;

/**
 * Copyright (c) 2019 leyou ALL Rights Reserved Project: learning Package: app.配置管理 Version: 1.0
 *
 * @author qingzhi.wu
 * @date 2020/6/21 11:24
 */
public class DataNode {
  private static final String Connect_String = "cdh01:2181,cdh02:2181,cdh03:2181";

  private static final int Session_Timeout = 4000;

  private static final String PARENT_NODE = "/config";

  private static List<String> oldChildNodeList = null;
  private static ZooKeeper zk = null;

  public static void main(String[] args) throws Exception {

    // 1. 获取zk连接

    zk =
        new ZooKeeper(
            Connect_String,
            Session_Timeout,
            new Watcher() {
              public void process(WatchedEvent event) {
                // 6.处理监听逻辑
                // 6.1 获取监听事件信息
                String path = event.getPath();
                Event.EventType type = event.getType();
                Event.KeeperState state = event.getState();
                // 为了屏蔽启动时有空值问题,给他屏蔽掉 也就是 type =None path = null
                if (state.getIntValue() == 3 && path != null) {
                  // 这里处理的意义也就是让他无限监听,不会中断
                  List<String> newChildrenNodeList = null;
                  // 获取触发后节点信息
                  try {
                    newChildrenNodeList = zk.getChildren(PARENT_NODE, true);
                  } catch (KeeperException e) {
                    e.printStackTrace();
                  } catch (InterruptedException e) {
                    e.printStackTrace();
                  }
                  // 判断子节点有变更
                  if (path.equals(PARENT_NODE) && type == Event.EventType.NodeChildrenChanged) {

                    // 处理添加逻辑
                    if (oldChildNodeList.size() < newChildrenNodeList.size()) {
                      // 此时说明有配置新增
                      // 获取新增
                      String diffBetweenTwoList =
                          getDiffBetweenTwoList(oldChildNodeList, newChildrenNodeList);
                      // 获取数据
                      byte[] data = null;
                      try {
                        data = zk.getData(PARENT_NODE + "/" + diffBetweenTwoList, true, null);
                      } catch (KeeperException e) {
                        e.printStackTrace();
                      } catch (InterruptedException e) {
                        e.printStackTrace();
                      }

                      System.out.println(
                          "服务器集群  add 了一项配置: key="
                              + diffBetweenTwoList
                              + ", value="
                              + new String(data));
                    }
                  } else {
                    // 此时处理删除逻辑,判断是否是删除操作,因为其中也包含更新操作
                    for (String s : oldChildNodeList) {
                      String childPath = PARENT_NODE + "/" + s;
                      if (path.equals(childPath) && type == Event.EventType.NodeDeleted) {
                        // 删除的配置的key
                        String diffServer =
                            getDiffBetweenTwoList(newChildrenNodeList, oldChildNodeList);
                        System.out.println("服务器集群  delete 了一项配置: key=" + diffServer);
                      }
                    }
                  }

                  // 此时只剩余更新操作
                  for (String child : oldChildNodeList) {
                    String childPath = PARENT_NODE + "/" + child;
                    if (path.equals(childPath) && Event.EventType.NodeDataChanged == type) {
                      byte[] data = null;
                      try {
                        data = zk.getData(childPath, true, null);
                      } catch (KeeperException e) {
                        e.printStackTrace();
                      } catch (InterruptedException e) {
                        e.printStackTrace();
                      }
                      String newValue = new String(data);
                      System.out.println(
                          "服务器集群  update 了一项配置: key=" + child + ", newValue=" + newValue);
                      break;
                    }
                  }
                  oldChildNodeList = newChildrenNodeList;
                }
              }
            });

    // 2.判断/config节点是否存在,不存在则创建

    if (zk.exists(PARENT_NODE, null) == null) {
      // 创建一个永久节点
      zk.create(PARENT_NODE, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    // 3.添加监听
    oldChildNodeList = zk.getChildren(PARENT_NODE, true);

    for (String childNode : oldChildNodeList) {
      // 将所有的子节点数据变更也加上监听
      String childNodePath = PARENT_NODE + "/" + childNode;

      zk.getData(childNodePath, true, null);
    }
    /**
     * 第四步: 让程序一致运行 当前的代码的执行分为两个线程运行 主线程 + 监听线程 (根据event的数据来判断掉地调用哪个watcher中的process方法运行) 最后的那些打印值都是
     * process方法中打印出来, 而且process方法的调用运行不在主线程中
     */
    Thread.sleep(Long.MAX_VALUE);
  }

  public static String getDiffBetweenTwoList(List<String> smallList, List<String> bigList) {

    // 默认实现: 增加了一个节点
    List<String> big = bigList;
    List<String> small = smallList;

    for (String val : big) {
      if (!small.contains(val)) {
        return val;
      }
    }

    return null;
  }
}
package de.apps.config;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

/**
 * Copyright (c) 2019 leyou ALL Rights Reserved Project: learning Package: app.配置管理 Version: 1.0
 *
 * @author qingzhi.wu
 * @date 2020/6/21 11:24
 */
public class Client {
  private static final String Connect_String =
          "cdh01:2181,cdh02:2181,cdh03:2181";

  private static final int Session_Timeout = 4000;

  private static final String PARENT_NODE = "/config";

  private static final String key = "name";
  private static final String value = "huangbo";
  private static final String value_new = "huangbo_copy";

  public static void main(String[] args) throws Exception {
    /** 第一步: 获取 zookeeper 连接 */
    ZooKeeper zk = new ZooKeeper(Connect_String, Session_Timeout, null);

    /** 第二步: 先判断 /config 节点存在与否 */
    Stat exists = zk.exists(PARENT_NODE, null);
    if (exists == null) {
      zk.create(PARENT_NODE, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    /** 第三部: 模拟实现 添加 修改 删除 */
    String path = PARENT_NODE + "/" + key;

    // 第三部: 增加一项配置
   // zk.create(path, value.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

    // 第三部: 修改一项配置
       // zk.setData(path, value_new.getBytes(), -1);

    // 第三部: 删除一项配置
          zk.delete(path, -1);

    zk.close();
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值