1、使用场景

配置中心一般用作系统的参数配置,它需要满足如下几个要求:高效获取、实时感知、分布式访问。对于一些少量频次访问的场景我们可以使用mysql数据库实现,但是有些参数在系统中访问频次较高,甚至是接口每访问一次就需要调起获取一次,特别在是大规模系统访问量的情况下,我们就需要一个高效获取实时感知的分布式配置中心。本章节我们使用zookeeper来实现一个分布式配置管理中心组件。

2、实现逻辑

实现的架构图如下所示,采取数据加载到内存方式解决高效获取的问题,借助zookeeper的节点监听机制来实现实时感知。


实现的逻辑流程图如下:分为新增配置,获取配置,删除配置。修改配置和删除配置的逻辑是一致的。

3、代码实现

接入层的接口定义如下:
/**
 * 
 */
package com.flykingmz.zookeeper.configyard;

import java.util.Map;

/**
 * 配置资源接口
 * @author flyking
 * 
 */
public interface ConfigYard {
	/**
	 * 配置平台根节点名称
	 */
	static String yardRoot = "/yard";
	
	/**
	 * 初始化配置
	 */
	void init();
	
	/**
	 * 重新加载配置资源
	 */
	void reload();
	
	/**
	 * 添加配置
	 * @param key
	 * @param value
	 */
	void add(String key, String value);

	/**
	 * 更新配置
	 * @param key
	 * @param value
	 */
	void update(String key, String value);

	/**
	 * 删除配置
	 * @param key
	 */
	void delete(String key);

	/**
	 * 获取配置
	 * @param key
	 * @return
	 */
	String get(String key);

	/**
	 * 获取所有的配置内容
	 * @return
	 */
	Map<String, String> getAll();

}
接口一个实现类:
/**
 * 
 */
package com.flykingmz.zookeeper.configyard;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.I0Itec.zkclient.ZkClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author flyking
 * 
 */
public class ProgrammaticallyConfigYard implements ConfigYard {
	private final static Logger logger = LoggerFactory
			.getLogger(ProgrammaticallyConfigYardTest.class);
	/**
	 * 存储配置内容
	 */
	private volatile Map<String, String> yardProperties = new HashMap<String, String>();

	private ZkClient client;
	
	private ConfigYardWatcher configYardWatcher;

	public ProgrammaticallyConfigYard(String serverstring) {
		this.client = new ZkClient(serverstring);
		configYardWatcher = new ConfigYardWatcher(client,this);
		this.init();
	}

	/**
	 * 初始化加载配置到内存
	 */
	public void init() {
		if(!client.exists(yardRoot)){
			client.createPersistent(yardRoot);
		}
		if (yardProperties == null) {
			logger.info("start to init yardProperties");
			yardProperties = this.getAll();
			logger.info("init yardProperties over");
		}
	}

	private String contactKey(String key){
		return yardRoot.concat("/").concat(key);
	}
	
	public void add(String key, String value) {
		String contactKey = this.contactKey(key);
		this.client.createPersistent(contactKey, value);
		configYardWatcher.watcher(contactKey);
	}

	public void update(String key, String value) {
		String contactKey = this.contactKey(key);
		this.client.writeData(contactKey, value);
		configYardWatcher.watcher(contactKey);
	}

	public void delete(String key) {
		String contactKey = this.contactKey(key);
		this.client.delete(contactKey);
	}

	public String get(String key) {
		if(this.yardProperties.get(key) == null){
			String contactKey = this.contactKey(key);
			if(!this.client.exists(contactKey)){
				return null;
			}
			return this.client.readData(contactKey);
		}
		return yardProperties.get(key);
	}

	public Map<String, String> getAll() {
		if(yardProperties != null){
			return yardProperties;
		}
		List<String> yardList = this.client.getChildren(yardRoot);
		Map<String, String> currentYardProperties = new HashMap<String, String>();
		for(String yard : yardList){
			String value = this.client.readData(yard);
			String key = yard.substring(yard.indexOf("/")+1);
			currentYardProperties.put(key, value);
		}
		return yardProperties;
	}

	public void reload() {
		List<String> yardList = this.client.getChildren(yardRoot);
		Map<String, String> currentYardProperties = new HashMap<String, String>();
		for(String yard : yardList){
			String value = this.client.readData(this.contactKey(yard));
			currentYardProperties.put(yard, value);
		}
		yardProperties = currentYardProperties;
	}

}
在其中使用到的一个zookeeper的监听实现类:
/**
 * 
 */
package com.flykingmz.zookeeper.configyard;

import java.util.List;

import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author flyking
 * 
 */
public class ConfigYardWatcher {
	private final static Logger logger = LoggerFactory
			.getLogger(ConfigYardWatcher.class);
	
	private ZkClient client;
	
	private ConfigYardListener configYardListener;
	
	private ConfigYard configYard;

	public ConfigYardWatcher(ZkClient client,ConfigYard configYard) {
		this.client = client;
		this.configYard = configYard;
		this.initConfigYard();
	}
	
	private void initConfigYard(){
		configYardListener = new ConfigYardListener();
	}  
	
	public void watcher(String key){
		client.subscribeDataChanges(key, configYardListener);
		client.subscribeChildChanges(key, configYardListener);
	}

	/**
	 * 配置监听器
	 * @author flyking
	 *
	 */
	private class ConfigYardListener implements IZkDataListener,IZkChildListener{
		public void handleDataChange(String dataPath, Object data)
				throws Exception {
			logger.info("data {} change,start reload configProperties",dataPath);
			configYard.reload();
		}

		public void handleDataDeleted(String dataPath) throws Exception {
			logger.info("data {} delete,start reload configProperties",dataPath);
			configYard.reload();
		}

		public void handleChildChange(String parentPath,
				List<String> currentChilds) throws Exception {
			logger.info("data {} ChildChange,start reload configProperties",parentPath);
			configYard.reload();
		}
		
	}
}
基于实现类的一个测试代码:
/**
 * 
 */
package com.flykingmz.zookeeper.configyard;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author flyking
 * 
 */
public class ProgrammaticallyConfigYardTest {
	private final static Logger logger = LoggerFactory
			.getLogger(ProgrammaticallyConfigYard.class);

	public static void main(String[] args) {
		ProgrammaticallyConfigYard yard = new ProgrammaticallyConfigYard("host:port");
		yard.add("testKey1", "1");
		yard.add("testKey2", "2");
		yard.add("testKey3", "3");
		yard.add("testKey4", "4");
		yard.add("testKey5", "5");
		yard.add("testKey6", "6");
		logger.info("value is===>"+yard.get("testKey1"));
		logger.info("value is===>"+yard.get("testKey2"));
		logger.info("value is===>"+yard.get("testKey3"));
		logger.info("value is===>"+yard.get("testKey4"));
		logger.info("value is===>"+yard.get("testKey5"));
		logger.info("value is===>"+yard.get("testKey6"));
		yard.update("testKey6", "testKey6");
		logger.info("update testKey6 value is===>"+yard.get("testKey6"));
		yard.delete("testKey1");
		yard.delete("testKey2");
		yard.delete("testKey3");
		yard.delete("testKey4");
		yard.delete("testKey5");
		yard.delete("testKey6");
	}
}

测试结果如下:
2016-10-26 14:58:39 [main] INFO com.flykingmz.zookeeper.configyard.ProgrammaticallyConfigYard  - value is===>1
2016-10-26 14:58:39 [main] INFO com.flykingmz.zookeeper.configyard.ProgrammaticallyConfigYard  - value is===>2
2016-10-26 14:58:39 [main] INFO com.flykingmz.zookeeper.configyard.ProgrammaticallyConfigYard  - value is===>3
2016-10-26 14:58:39 [main] INFO com.flykingmz.zookeeper.configyard.ProgrammaticallyConfigYard  - value is===>4
2016-10-26 14:58:39 [main] INFO com.flykingmz.zookeeper.configyard.ProgrammaticallyConfigYard  - value is===>5
2016-10-26 14:58:39 [main] INFO com.flykingmz.zookeeper.configyard.ProgrammaticallyConfigYard  - value is===>6
2016-10-26 14:58:39 [ZkClient-EventThread-9-172.16.10.58:2181] INFO com.flykingmz.zookeeper.configyard.ConfigYardWatcher  - data /yard/testKey6 change,start reload configProperties
2016-10-26 14:58:39 [main] INFO com.flykingmz.zookeeper.configyard.ProgrammaticallyConfigYard  - update testKey6 value is===>testKey6
2016-10-26 14:58:39 [ZkClient-EventThread-9-172.16.10.58:2181] INFO com.flykingmz.zookeeper.configyard.ConfigYardWatcher  - data /yard/testKey6 delete,start reload configProperties

本系统的实现源码可以从https://github.com/flykingmz/zookeeper-step获取,具体的项目名称为:configYard





Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐