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.3
192.168.1.1
192.168.1.2
192.168.1.1被更新了          

posted @ 2017-05-16 17:40  猿祖  阅读(792)  评论(0编辑  收藏  举报