近在工作中,为了完善公司集群服务的架构,提高可用性,降低运维成本,因此开始学习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");
  }
  }