Zookeeper配置管理

一、简介

Zookeeper 从设计模式角度来看,是一个基于观察者模式设计的分布
式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应,从而实现集群中类似 Master/Slave 管理模式

二、配置管理(Configuration Management)

配置文件通常有如下几种常用的保存方式:
1.将配置信息保存在程序代码中
这种方案简单,但每次修改配置都要重新编译、部署应用程序。
2.将配置信息保存在xml文件或者属性文件中
在参数信息保存在xml或者属性文件中,当需要修改参数时,直接修改 xml 文件。这样无需重新编译,只需重新部署修改的文件即可。
3.将配置信息保存在数据库中
当需要修改配置信息时,直接修改数据库中的数据,然后重启应用程序,或者刷新应用的缓存。

小结:上面的几种方法比较适用于普通的单服务项目;但是, 配置的管理在分布式应用环境中很常见,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样非常麻烦而且容易出错。
像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中。

配置管理结构图

这里写图片描述

三、zookeeper的监听事件

原生zookeeper的事件监听采用Watcher实现,不过Watcher监听是一次性的,如果需要继续监听该事件,必须重新注册。

Curator中采用cache来封装了对事件的监听,在包org.apache.curator.framework.recipes.cache封装了3种类型的事件监听。
cache 分为三种(其实就是从不同的维度去解析cache):
1.PathChildrenCache cache 监听某一个path,一个znode的子节点
2.node cache 监听某一个node
3.tree cache 监听节点以及子节点的状态,监控整个树中的节点
通过addListener添加事件监听,监听变化,提供接口以方便实现者做出相关改变。

下面的代码是apache的curator包里面的api(还有org.I0Itec.zkclient包中的subscribeDataChanges方法可以监听节点的变化,这里不具体描述)。
代码功能图
这里写图片描述

package app.curator;

import org.apache.commons.lang.StringUtils;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.eclipse.jetty.util.ConcurrentHashSet;


import java.lang.reflect.Constructor;
import java.nio.charset.Charset;
import java.util.*;

/**
 * Created by lili19289 on 2016/8/18.
 */
public class ZooKeeperClient {

    private static final Logger LOG = Logger.getLogger(ZooKeeperClient.class);

    public static final char PATH_SEPARATOR_CHAR = '/';

    public static final int DEFAULT_BASE_SLEEP_TIME_MS = 1000;

    public static final int DEFAULT_MAX_RETRIES = 0;

    private ConnectionState connectionState;//连接状态

    //连接状态监听
    private Set<ZooKeeperClientConnectionStateListener> connectionStateListeners = new ConcurrentHashSet<ZooKeeperClientConnectionStateListener>();

    private Map<String,ZooKeeperNodeCacheHandler> nodeCacheHandlerMap = new HashMap();
    private CuratorFramework client;

    /**
     * 创建ZooKeeper客户端。
     * @param connectString 服务器连接串。
     * @param retryPolicy 重试策略。
     */
    public ZooKeeperClient(String connectString, RetryPolicy retryPolicy) {
        client = CuratorFrameworkFactory.newClient(connectString, retryPolicy);
    }

    /**
     * 创建ZooKeeper客户端。
     * @param connectString 服务器连接串。
     * @param baseSleepTimeMs 重试间隔时间。
     * @param maxRetries 最大重试次数。
     */
    public ZooKeeperClient(String connectString, int baseSleepTimeMs, int maxRetries) {
        client = CuratorFrameworkFactory.newClient(connectString, new ExponentialBackoffRetry(baseSleepTimeMs,
                maxRetries));
    }

    /**
     * 创建ZooKeeper客户端。
     * @param connectString 服务器连接串。
     */
    public ZooKeeperClient(String connectString) {
        try {
        client = CuratorFrameworkFactory.newClient(connectString, new ExponentialBackoffRetry(
                DEFAULT_BASE_SLEEP_TIME_MS, DEFAULT_MAX_RETRIES));
        }catch (Exception e){
            LOG.error("zookeeperclient connect failed ,the connectString is "+connectString,e);
        }
    }

    /**
     * 启动该ZooKeeper客户端。
     */
    public void start() {
        try {
            // 先确认状态,如果已经启动或关闭时调用start会抛异常
            if (CuratorFrameworkState.LATENT == client.getState()) {
                client.getConnectionStateListenable().addListener(new ClientConnectionStateListener());
                client.start();
            }
        }catch (Exception e){
            LOG.error("zookeeperclient start failed ",e);
        }

    }

    /**
     * 关闭该ZooKeeper客户端。
     */
    public void close() {
        client.close();
    }


    /**
     * 创建指定路径,永久路径
     * @param path 路径。
     */
    public void createPersistent(String path) {
        create(path, CreateMode.PERSISTENT);
    }

    /**
     * 创建指定路径,临时路径
     * @param path 路径。
     */
    public void createEphemeral(String path) {
        create(path, CreateMode.EPHEMERAL);
    }

    /**
     * 创建指定路径,如果父路径不存在则抛出异常。
     * @param path 路径。
     * @param createMode 要创建的路径的类型。
     */
    public void create(String path, CreateMode createMode) {
        // 先检查路径是否存在,如果已存在则不做操作
        if (!exists(path)) {
            try {
                client.create().creatingParentsIfNeeded()//如果指定的节点的父节点不存在,递归创建父节点
                        .withMode(createMode)//存储类型(临时的还是持久的)
                        .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)//访问权限
                        .forPath(path);
            } catch (Exception e) {
                String message = "create path failed, path=" + path;
                LOG.error(message, e);
                throw new RuntimeException(message, e);
            }
        }
    }


    /**
     * 删除指定路径,会递归删除其下子路径,如果路径不存在则不操作。
     * @param path 路径。
     */
    public void delete(String path) {
        // 先检查路径是否存在,如果不存在则不做操作
        if (exists(path)) {
            // 递归删除子路径
            delete(path, true);
        }
    }

    /**
     * 删除指定路径,如果路径不存在,则抛出异常。
     * @param path 路径。
     * @param clearChildren 如果为true,则递归删除子路径,否则只删除指定路径,如果路径非空,则抛出异常。
     */
    public void delete(String path, boolean clearChildren) {
        try {
            if (clearChildren) {
                // 列出所有子路径
                List<String> children = client.getChildren().forPath(path);
                if (!children.isEmpty()) {
                    // 遍历子路径操作
                    for (String childPath : children) {
                        // 递归调用删除子路径
                        delete(path + PATH_SEPARATOR_CHAR + childPath, true);
                    }
                }
            }
            // 删除本路径
            client.delete().forPath(path);
        } catch (Exception e) {
            String message = "delete path failed, path=" + path;
            LOG.error(message, e);
            throw new RuntimeException(message, e);
        }
    }

    /**
     * 检查指定路径是否存在。
     * @param path 路径。
     * @return 如果存在则返回true,否则返回false。
     */
    public boolean exists(String path) {
        try {
            // 如果返回的Stat不为null则表示该路径存在
            return null != client.checkExists().forPath(path);
        } catch (Exception e) {
            String message = "check exists failed, path=" + path;
            LOG.error(message, e);
            throw new RuntimeException(message, e);
        }
    }


    /**
     * 获取指定路径中的子路径名。
     * @param path 要获取子路径的父路径。
     * @return 子路径名,如果父路径不存在,则返回空白列表。
     */
    public List<String> getChildren(String path) {
        try {
            // 先检查路径是否存在,如果存在则获取子路径名
            if (exists(path)) {
                return client.getChildren().forPath(path);
            } else {// 如果该路径不存在则返回空列表
                return Collections.emptyList();
            }
        } catch (Exception e) {
            String message = "get children failed, path=" + path;
            LOG.error(message, e);
            throw new RuntimeException(message, e);
        }
    }

    public void mkdirs(String path) {
        mkdirs(path, CreateMode.PERSISTENT);
    }

    /**
     * 创建路径,如果父路径不存在,会自动创建。
     * @param path 路径。
     * @param createMode 要创建的路径的类型。
     */
    public void mkdirs(String path, CreateMode createMode) {
        // 先检查路径是否存在,如果已存在则不做操作
        if (!exists(path)) {
            try {
                client.create().creatingParentsIfNeeded().withMode(createMode).forPath(path);
            } catch (Exception e) {
                String message = "create path failed, path=" + path;
                LOG.error(message, e);
                throw new RuntimeException(message, e);
            }
        }
    }

    public  void setData( String path, String data) {
        try{
            if(!exists(path)){
                mkdirs(path);
            }
            if(StringUtils.isBlank(data)) client.setData().forPath(path);
            // set data for the given node
            client.setData().forPath(path,data.getBytes());
        }catch(Exception e){
            String message = "set data failed, path=" + path;
            LOG.error(message, e);
        }

    }

    /**
     * 获取指定路径上的数据。
     * @param path 路径。
     * @return 路径上的数据,如果路径不存在,则返回null。
     */
    public String getStringData(String path) {
        byte[] bytes = getData(path);
        return null == bytes ? null : new String(bytes, Charset.forName("UTF-8"));
    }
    /**
     * 获取指定路径上的数据。
     * @param path 路径。
     * @return 路径上的数据,如果路径不存在,则返回null。
     */
    public byte[] getData(String path) {
        try {
            // 先检查路径是否存在,如果存在则获取节点数据
            if (exists(path)) {
                byte[] bytes = client.getData().forPath(path);
                if (null == bytes) {
                    return null;
                }
                return bytes;
            } else {// 如果路径不存在则返回null
                return null;
            }
        } catch (Exception e) {
            String message = "get data failed, path=" + path;
            LOG.error(message, e);
            throw new RuntimeException(message, e);
        }
    }


    public CuratorFramework getCuratorFramework(){
        return client;
    }


    /**
     * node监听,一个监听方法
     * 该方法是直接实现NodeCacheListener来实现监听
     * @param path
     * @param listenerClass
     */
    public void addNodeCacheListener(String path,Class<? extends ZooKeeperNodeCacheListener> listenerClass){
        final NodeCache nodeCache = new NodeCache(client, path);
        Class[] paramTypes = {NodeCache.class};
        Object[] params = {nodeCache};
        Constructor con=null;
        ZooKeeperNodeCacheListener listener=null;
        try {
            con = listenerClass.getConstructor(paramTypes);
            listener =(ZooKeeperNodeCacheListener) con.newInstance(params);
            nodeCache.getListenable().addListener(listener);
            nodeCache.start(true);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    /**
     * node监听,一个或以上监听方法
     * 该方法是通过实现NodeCacheListener,然后对自己定义的NodeCahceChangeListener的接口进行调用来实现的监听
     * @param path
     * @param listeners
     */
    public void addNodeCacheListeners(String path, ZooKeeperNodeCacheHandler.NodeCahceChangeListener...listeners){
        ZooKeeperNodeCacheHandler nodeCacheListeners = getZooKeeperNodeCacheListeners(path);
        nodeCacheListeners.addListeners(listeners);
    }

    private ZooKeeperNodeCacheHandler getZooKeeperNodeCacheListeners(String path){
        ZooKeeperNodeCacheHandler nodeCacheListeners =nodeCacheHandlerMap.get(path);
        if(nodeCacheListeners==null){
            NodeCache nodeCache = new NodeCache(client,path);
            nodeCacheListeners = new ZooKeeperNodeCacheHandler(path,nodeCache);
            nodeCacheListeners.startWatch();
            nodeCacheHandlerMap.put(path,nodeCacheListeners);
        }
        return nodeCacheListeners;
    }

    /**
     * Path Cache:
     * 监视一个路径下
     * 1)孩子结点的创建、2)孩子结点的删除,3)以及孩子结点数据的更新。
     *  产生的事件会传递给注册的PathChildrenCacheListener。
     * @param path
     * @param listener
     */
    public void addPathChildrenListener(String path,PathChildrenCacheListener listener){
        if(!exists(path))mkdirs(path);
        try {
            PathChildrenCache childrenCache = new PathChildrenCache(client, path,true);
            childrenCache.getListenable().addListener(listener);
            childrenCache.start();
//            childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        } catch (Exception e) {
            e.printStackTrace();
            LOG.error("PathChildrenCache listen fail,path = "+path,e);
        }

    }

    /**
     *  监控 指定节点和节点下的所有的节点的变化--无限监听  可以进行本节点的删除(不在创建)
     * @param path
     * @param listener
     */
    public void addTreeCacheListener(String path, TreeCacheListener listener){
        if(!exists(path))mkdirs(path);
        try {
            TreeCache treeCache = new TreeCache(client,path);
            treeCache.getListenable().addListener(listener);
            treeCache.start();
        }catch (Exception e){
            LOG.error(" zookeeper treeCache listener failed ,the path is "+path,e);
        }
    }

    /*
    ********************************************************************************************************8
     */

    /**
     * 连接状态监听实现
     */
    private class ClientConnectionStateListener implements ConnectionStateListener {

        public void stateChanged(CuratorFramework client, ConnectionState newState) {
            connectionState = newState;
            notifyConnectionStateListener(newState);
        }

    }

    private void notifyConnectionStateListener(ConnectionState state) {
        if (state.isConnected()) {
            for (ZooKeeperClientConnectionStateListener listener : connectionStateListeners) {
                listener.notifyConnected();
            }
        } else {
            for (ZooKeeperClientConnectionStateListener listener : connectionStateListeners) {
                listener.notifyDisconnected();
            }
        }
    }

    private void notifyConnectionStateListener(ConnectionState state,
                                               ZooKeeperClientConnectionStateListener listener) {
        if (state.isConnected()) {
            listener.notifyConnected();
        } else {
            listener.notifyDisconnected();
        }
    }

    /**
     * 添加客户端连接状态监听
     * @param listener 客户端连接状态监听器
     */
    public void addClientConnectionStateListener(ZooKeeperClientConnectionStateListener listener) {
        // 任何情况下都添加,启动后才通知
        if (connectionStateListeners.add(listener) && CuratorFrameworkState.STARTED == client.getState()) {
            notifyConnectionStateListener(connectionState, listener);
        }
    }

}



package app.curator;

import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;

/**
 * Created by lili19289 on 2016/8/19.
 * NodeCache主要用来监听节点本身的变化,当节点的状态发生变更后,会回调NodeCachaListener
 * 可以继承这个类进行具体操作
 */
public class ZooKeeperNodeCacheListener implements NodeCacheListener {

    protected NodeCache nodeCache;

    public ZooKeeperNodeCacheListener(NodeCache nodeCache){
        this.nodeCache = nodeCache;
    }

    public void nodeChanged() throws Exception {
        System.out.println("NodeCache changed, data is: " + new String(nodeCache.getCurrentData().getData()));
    }
}


package app.curator;

import app.utils.IoUtil;
import org.apache.commons.io.IOUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.log4j.Logger;


/**
 * Created by lili19289 on 2016/8/20.
 * 监视一个路径下
 * 1)孩子结点的创建、2)孩子结点的删除,3)以及孩子结点数据的更新。
 * 不监听自己节点的操作
 * 产生的事件会传递给注册的PathChildrenCacheListener。
 */
public class ZooKeeperPathChildrenCacheListener implements PathChildrenCacheListener {
    private static final Logger LOG = Logger.getLogger(ZooKeeperPathChildrenCacheListener.class);
    @Override
    public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent)  {
        ChildData data = pathChildrenCacheEvent.getData();
        try{
            PathChildrenCacheEvent.Type eventType = pathChildrenCacheEvent.getType();
            switch (eventType) {
                case CHILD_ADDED:
                    System.out.println("CHILD_ADDED : "+ data.getPath() +"  数据:"+ new String(data.getData(), IoUtil.CHARSET_UTF8));
                    break;
                case CHILD_REMOVED:
                    System.out.println("CHILD_REMOVED : "+ data.getPath() +"  数据:"+ new String(data.getData(), IoUtil.CHARSET_UTF8));
                    break;
                case CHILD_UPDATED:
                    System.out.println("CHILD_UPDATED : "+ data.getPath() +"  数据:"+ new String(data.getData(), IoUtil.CHARSET_UTF8));
                    break;
                default:
                    break;
            }
        }catch (Exception e){
            LOG.error("ZooKeeperPathChildrenCacheListener listen failed,the path is "+data.getPath() ,e);
        }

    }
}

package app.curator;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
import org.apache.log4j.Logger;

/**
 * Created by lili19289 on 2016/8/20.
 */
public class ZooKeeperTreeCacheListener implements TreeCacheListener {
    private static final Logger LOG = Logger.getLogger(ZooKeeperTreeCacheListener.class);

    public void childEvent(CuratorFramework client, TreeCacheEvent event) {
            ChildData data = event.getData();
            try {
                if (data != null) {
                    switch (event.getType()) {
                        case NODE_ADDED:
                            System.out.println("treeCache ----- NODE_ADDED : " + data.getPath() + "  数据:" + new String(data.getData()));
                            break;
                        case NODE_REMOVED:
                            System.out.println("treeCache -----  NODE_REMOVED : " + data.getPath() + "  数据:" + new String(data.getData()));
                            break;
                        case NODE_UPDATED:
                            System.out.println("treeCache -----  NODE_UPDATED : " + data.getPath() + "  数据:" + new String(data.getData()));
                            break;

                        default:
                            break;
                    }
                } else {
                    System.out.println("data is null : " + event.getType());
                }
            }
        catch(Exception e) {
            LOG.error("ZooKeeperTreeCacheListener listen failed,the path is "+data.getPath() ,e);
        }
    }
}

package app.curator;

import app.context.RuntimeContext;
import org.apache.log4j.Logger;

/**
 * Created by lili19289 on 2016/8/19.
 */
public class ZooKeeperLoader {
    private static final Logger LOG = Logger.getLogger(ZooKeeperLoader.class);

    ZooKeeperClient zooKeeperClient;
    public void init(){
        try {
            zooKeeperClient =  RuntimeContext.getBean(ZooKeeperClient.class);
            zooKeeperClient.addNodeCacheListener("/learning",ZooKeeperNodeCacheListener.class);
//            zooKeeperClient.addPathChildrenListener("/learning",new ZooKeeperPathChildrenCacheListener());
//            zooKeeperClient.addTreeCacheListener("/learning",new ZooKeeperTreeCacheListener());
//            zooKeeperClient.addNodeCacheListener("/learning");
        }catch (Exception e){
            e.printStackTrace();
            LOG.error("ZooKeeperLoader connect zookeeper failed ",e);
        }

    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值