ZooKeeper实践:(2)配置管理
一、 前言
配置是每个程序不可或缺的一部分,配置有多重方式:xml、ini、property、database等等,从最初的单机环境到现在的分布式环境。
1. 以文件的格式存储配置,修改任何都要改程序,重新发布,重新部署,经常出现数据不一致的问题,配置错误,会造成更大的问题。
2. 以数据库+缓存配置,解决了动态更新配置的方式,但是存在数据库的单点问题,一单数据库出现问题,更新的操作都会失败,造成配置不能持久化。
随着机器数的增多,逐个修改配置是一件不合理的做法,使用Zookeeper可以实现数据发布与订阅,顾名思义就是把数据存储在Zookeeper的节点上,供订阅方进行获取,实现配置信息的集中式管理和动态更新。解决以上问题
二、需求
1. 实现集中式配置管理
2. 实现灰度发布(先发布一台机器)
三、技术原理
1. 配置信息存储在Zookeeper的某个目录下中,目录的命名按照一定的规则(例如:应用ID),一组相同功能的应用集群监控一个节点。
2. 配置发生变化,应用服务器会监听到事件,然后从Zookeeper获取新的配置信息到更新应用服务器的本地缓存中。
3. 做一个简单的界面展示集群的所有机器,可以拉出来一台灰度发布。
4. 应用服务器在接收到新的配置信息时判断是否是灰度发布的,如果是判断机器的IP,如果IP相同就更新本地缓存,否则不更新。
四:架构体系

五:流程图

六:相关代码
client模拟:
package com.zk.config.manager;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
public class App {
	private CountDownLatch connectedSemaphore = new CountDownLatch(1);
	private ZooKeeper zooKeeper;
	private Object lock = new Object();
	private String ip;
	private String rootConfig;
	public App(String ip, String root) {
		this.ip = ip;
		this.rootConfig = root;
		this.zooKeeper = connectZookeeper();
		
	}
	/**
	 * 更新缓存
	 * @param path
	 * @param value
	 */
	private void updateCache(String path, String value) {
		//System.out.println(CacheManager.get("ips"));
		if (CacheManager.get("ips").contains(this.ip)) {
			CacheManager.add(this.ip + path, value);
			System.out.println(ip+"被更新了");
		}
	}
	
	public  ZooKeeper connectZookeeper() {
		synchronized (lock) {
			if (zooKeeper == null) {
				try {
					zooKeeper = new ZooKeeper("192.168.1.222:2181", 5000, new Watcher() {
						public void process(WatchedEvent event) {
							if (KeeperState.SyncConnected == event.getState()) {
								if (EventType.None == event.getType() && null == event.getPath()) {
									try {
										connectedSemaphore.countDown();
										zooKeeper.getChildren(rootConfig, true);
									} catch (KeeperException e) {
										e.printStackTrace();
									} catch (InterruptedException e) {
										e.printStackTrace();
									}
								} else if (EventType.NodeChildrenChanged == event.getType()) {
									try {
										System.out.println(ip);
										String path = event.getPath();
										String value  =new String(zooKeeper.getData(path, false, null));
										zooKeeper.getChildren(rootConfig, true);
										updateCache(path,value);//模拟更新缓存
									} catch (KeeperException e) {
										e.printStackTrace();
									} catch (InterruptedException e) {
										e.printStackTrace();
									}
								}
							}
						}
					}
					);
					connectedSemaphore.await();
				} catch (IOException e) {
					e.printStackTrace();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		return zooKeeper;
	}
}  本地缓存模拟:
package com.zk.config.manager;
import java.util.HashMap;
import java.util.Map;
//模拟本机缓存
public class CacheManager {
	
	private static Map<String, String> map = new HashMap<String, String>();
	
	
	public static void add(String key,String value)
	{
		map.put(key, value);
	}
	
	
	public static String get(String key)
	{
		return map.get(key);
	}
} 配置管理模拟:
package com.zk.config.manager;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class ConfigManager {
    private static String rootConfigName = "/testApp";
    public static void main(String[] args) throws KeeperException, InterruptedException {
        String ips = args[0]; // 要发布的机器IP
        CacheManager.add("ips", ips);
        App app1 = new App("192.168.1.1", rootConfigName);
        App app2 = new App("192.168.1.2", rootConfigName);
        App app3 = new App("192.168.1.3", rootConfigName);
        ZooKeeper zooKeeper = app1.connectZookeeper();
        zooKeeper.create(rootConfigName + "/gggggg", "sds".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        zooKeeper.delete(rootConfigName + "/gggggg", -1);
        Thread.sleep(60000);
    }
} 运行结果:192.168.1.3192.168.1.1192.168.1.2192.168.1.1被更新了
