zookeeper 分布式配置中心

请相信我,你必定会更优秀!html

 背景:系统基于分布式构建,比方如今两台服务,A,B,系统中不少配置文件是共享的。若是修改配置文件,则须要将A,B两个服务上的配置文件进行修改。若是业务扩展,未来有100个服务,1000个服务呢,配置文件的更新无疑成了一件麻烦事。因此咱们必需要有一个配置中心,好比 db,redis等,全部服务都去配置中心获取配置信息,因为配置信息改动不是很频繁,咱们第一次从 db获取到配置信息后,会将它们进行保留(比方放在map中),不至于每次都去 db拉取。请注意,一个新的问题出现了,若是配置中心的某个配置进行了修改,咱们服务中的配置信息如何作到实时性?重启一下线上服务吗?你可能又会想到,开启一个定时线程,固定时间间隔去更新,时间间隔越小,越能保证咱们服务中配置信息的实时性,1m,1s?每1秒去 db拉取一次吗?java

解决方案:zookeeper做配置中心,数据结构适合咱们存储配置信息,监控机制保证客户端数据实时性。node

官网摘抄(https://zookeeper.apache.org/doc/r3.5.4-beta/zookeeperOver.html):web

Conditional updates and watches
ZooKeeper supports the concept of watches. Clients can set a watch on a znode. A watch will be triggered and removed when the znode changes. When a watch is triggered, the client receives a packet saying that the znode has changed. If the connection between the client and one of the Zoo Keeper servers is broken, the client will receive a local notification. These can be used to [tbd].

若是你对 zookeeper不了解,请您先移步官网。zookeeper官网redis


开始演示spring

zookeeper 添加配置信息apache

pom中添加maven依赖:bash

<dependency>
	<groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
	<version>3.4.9</version>
</dependency>

一、业务配置信息数据结构

package net.uuimi.blog;

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

/**
 * function: 配置信息
 * 
 * @author zhanghaolin
 * @date 2018年12月22日下午12:49:43
 */
public class Constant {

	public static Map<String, Object> map = new HashMap<String, Object>();

	static {
		map.put("private_key", ""); // 配置1
		map.put("public_Key", "");  // 配置2
	}
}

二、 监控中心maven

package net.uuimi.blog;

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.InitializingBean;


/**
 * function: 监控中心
 * 
 * @author zhanghaolin
 * @date 2018年12月22日下午12:52:37
 */
public class ConstantMonitor implements Watcher, InitializingBean {

	private static ZooKeeper zookeeper = null;
	private static ConstantMonitor constantMonitor = null;
	private static CountDownLatch countDownLatch = new CountDownLatch(1);
	private static Stat stat = new Stat();

	/**
	 * zookeeper链接&初始化配置信息
	 */
	public void afterPropertiesSet() throws Exception {

		constantMonitor = new ConstantMonitor();
		try {
			zookeeper = new ZooKeeper("127.0.0.1:2181", 2000, constantMonitor); // 我这里用单点演示
			countDownLatch.await();

			System.out.println("=====================》》 Zookeeper初始化配置信息:");
			Iterator<Entry<String, Object>> iterator = Constant.map.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<String, Object> item = iterator.next();
				String name = item.getKey();
				byte[] dataBs = zookeeper.getData("/" + name, true, stat);
				item.setValue(new String(dataBs, "UTF-8"));
				System.out.println(name + "=" + new String(dataBs, "UTF-8"));
			}
			System.out.println("《《===================== Zookeeper初始化配置信息完毕!");
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 监控&更新
	 */
	public void process(WatchedEvent event) {
		String path = event.getPath();
		KeeperState state = event.getState();
		EventType type = event.getType();
		if (type == EventType.None && state == KeeperState.SyncConnected) {
			System.out.println("=====================》》 Zookeeper链接成功!");
			countDownLatch.countDown();
		} else if (type != EventType.None) {
			try {
				byte[] dataBs = zookeeper.getData(path, true, stat);
				String name = path.substring(1);
				Constant.map.put(name, new String(dataBs, "UTF-8"));
				System.out.println(String.format("=====================》》  配置信息【update】:%s=%s", name, new String(dataBs, "UTF-8")));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

三、spring扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd">

	<bean class="com.web.EnvConstants.ConstantMonitor" />

</beans>

意在服务启动的时候,装载spring.xml,进行配置文件的初始化。

ok!演示以下

package net.uuimi.blog;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * function: 测试
 * @author zhanghaolin
 * @date 2018年12月22日下午10:18:07
 */
public class Test {

	@SuppressWarnings("resource")
	public static void main(String[] args) throws InterruptedException {
		
		ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("/springtest.xml");
		
		Thread.sleep(Integer.MAX_VALUE);
	}
	
}

控制台输出:

请注意,关键点到了,咱们尝试修改配置信息,看程序可否保证信息实时更新

一、咱们在zookeeper修改配置信息

二、控制台监听,输出

 

完工。  

===

遗留问题:细心的你,能够发现,这样写是不支持新增配置信息的同步的。

解决方案:参考zookeeper的子节点监听事件。具体代码再也不呈现。

  努力改变本身和身边人的生活。

特别但愿本文能够对您有所帮助,转载请注明出处。感谢你们留言讨论交流。