最近在工作中,为了完善公司集群服务的架构,提高可用性,降低运维成本,因此开始学习ZooKeeper。
至于什么是ZooKeeper?它能做什么?如何安装ZooKeeper?我就不一一介绍了,类似这些资料网上到处都是。我主要是把在开发过程中,以及个人对ZooKeeper的一些了解记录下来,大家如果遇到类似场景时,希望我的文章能够给你提供一些思路。
我使用的ZooKeeper(以下简称:ZK)客户端是Curator Framework,是Apache的项目,它主要的功能是为ZK的客户端使用提供了高可用的封装。在Curator Framework基础上封装的curator-recipes,实现了很多经典场景。比如:集群管理(Leader选举)、共享锁、队列、Counter等等。可以总结Curator主要解决以下三类问题:
封装ZK Client与Server之间的连接处理;
提供了一套Fluent风格的操作API;
提供ZK各种应用场景的抽象封装;
本文主要完成的目标是:Spring PropertyPlaceholderConfigurer配置文件加载器集成ZooKeeper来实现远程配置读取。
配置管理(Configuration Management)。
在集群服务中,可能都会遇到一个问题:那就是当需要修改配置的时候,必须要对每个实例都进行修改,这是一个很繁琐的事情,并且易出错。当然可以使用脚本来解决,但这不是最好的解决办法。
OK,Let's go!
我们先看看项目结构
ZooKeeperPropertyPlaceholderConfigurer.java
继承org.springframework.beans.factory.config.PropertyPlaceholderConfigurer,重写processProperties(beanFactoryToProcess, props)来完成远端配置加载的实现
package org.bigmouth.common.zookeeper.config.spring; import java.io.UnsupportedEncodingException; import java.util.Properties; import org.apache.commons.lang.StringUtils; import org.bigmouth.common.zookeeper.config.Config; import org.bigmouth.common.zookeeper.config.ZooKeeperConfig; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; public class ZooKeeperPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer { public static final String PATH = "zoo.paths"; @Override protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException { super.processProperties(beanFactoryToProcess, props); try { fillCustomProperties(props); System.out.println(props); } catch (Exception e) { // Ignore e.printStackTrace(); } } private void fillCustomProperties(Properties props) throws Exception { byte[] data = getData(props); fillProperties(props, data); } private void fillProperties(Properties props, byte[] data) throws UnsupportedEncodingException { String cfg = new String(data, "UTF-8"); if (StringUtils.isNotBlank(cfg)) { // 完整的应该还需要处理:多条配置、value中包含=、忽略#号开头 String[] cfgItem = StringUtils.split(cfg, "="); props.put(cfgItem[0], cfgItem[1]); } } private byte[] getData(Properties props) throws Exception { String path = props.getProperty(PATH); Config config = new ZooKeeperConfig(); return config.getConfig(path); } } |
Config.java
配置操作接口
package org.bigmouth.common.zookeeper.config;
public interface Config {
byte[] getConfig(String path) throws Exception;
}
Startup.java
程序启动入口
package org.bigmouth.common.zookeeper.config;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Startup {
public static void main(String[] args) {
new ClassPathXmlApplicationContext("classpath:/config/applicationContext.xml");
}
}