配置中心

作为服务器开发人员,对配置文件一定不陌生,也一定曾经踩过很多配置文件的坑。当服务器集群数量较少,系统采用all in one的架构时,我们可以采用每台服务器单独使用本地配置的方法,配置文件跟发布包一起采用用VCS管理。但是随着服务器集群的扩展,当服务器数量变的庞大时,all in one的架构会变的笨重臃肿,带来更新发布流程复杂,雪崩风险升高等问题。这时all in one的架构就不那么适用了。我们需要考虑对系统进行改造,做微服务化的分解。这时配置文件和配置项等就不再适合使用前面的方案。而是需要一个独立的配置中心对所有微服务的配置进行控制。

目前开源世界中已经有许多成熟的配置中心实现方案,如阿里的Diamond、360的QConf、spring的Cloud Config、百度的disconf等等。其中的区别这里就不一一对比了,每个都能完成对应的任务,选择适合自己情况的即可。由于我们服务90%是基于java的,disconf的推模式比Diamond的拉模式在效率上要高一些,同时disconf也更便于做二次开发,所以选择了disconf。

实现配置中心

配置中心的功能很简单,一句话概括就是:负责管理所有服务的配置。

源码编译打包

首先到github上找到disconf的源码,并fork一份到自己的git仓库(这样可以便于自己调试开发),然后在用于部署配置中心的服务器上clone一份源码。目录结构如下:

/Users/lc/disconf/
    + disconf-src
    + resources
    + war
    + deploy.sh

disconf-src是源码目录,resources是配置目录,包含以下四个配置文件:

  • jdbc-mysql.properties (数据库配置)
  • redis-config.properties (Redis配置)
  • zoo.properties (Zookeeper配置)
  • application.properties (应用配置)

war是部署目录。deploy.sh是打包脚本,内容如下:

ONLINE_CONFIG_PATH=/Users/lc/disconf/resources
WAR_ROOT_PATH=/Users/lc/disconf/war
export ONLINE_CONFIG_PATH
export WAR_ROOT_PATH
cd disconf-src/disconf-web
sh deploy/deploy.sh

运行deploy.sh脚本进行编译打包,最后会在war文件夹中生成部署用的文件。

部署

部署之前需要先安装依赖的软件:MySQL、Tomcat、Nginx、zookeeper、Redis(如果是线上环境使用,那么这些依赖软件的部署需要考虑高可用问题),具体安装过程这里就不说了。

数据库准备

安装完mysql之后,需要对数据库进行初始化。先后执行disconf-src/disconf-web/sql中的sql文件:

  • 1-init_table.sql 主要是生成tables
  • 2-data.sql 主要是生成测试数据
  • 20141201/disconf.sql 升级,支持 管理员角色、用户邮箱
  • 20141226/disconf.sql 升级,支持 URL权限控制
  • 20150101/disconf.sql 增加一个测试数据
  • 20150320/disconf.sql 增加reloadable config的测试文件

zookeeper准备

安装好zookeeper集群后,在resources目录下的zoo.properties中配置好所有的zookeeper地址,并指定路径前缀:

hosts=127.0.0.1:8581 # 测试用,所以只配置了一台zookeeper
zookeeper_url_prefix=/disconf

redis准备

redis在这里只作为账号登录缓存使用,所以没有也没关系,可以先不部署。

tomcat部署

数据库准备好之后,接着部署tomcat,在server.xml文件的host节点下增加Context:<Context path="" docBase="/Users/lc/disconf/war"/>设定好tomcat端口后启动tomcat

nginx部署

disconf-web的部署采用了前后端完全分离的模式,所以前端页面直接通过nginx访问。在nginx中增加如下配置:

upstream disconf {
    server 127.0.0.1:8085;
}

server {
    listen       8081;
    server_name  localhost;

    location / {
        root /Users/lc/disconf/war/html;
        if ($query_string) {
            expires max;
        }
    }

    location ~ ^/(api|export) {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://disconf;
    }
}

启动

上述过程完成后,访问nginx的监听地址即可进入disconf的管理页面。

应用

部署完disconf-web,接下来我们需要在自己的服务中集成disconf-clent实现配置管理。disconf-client分为注解式注入和xml配置文件注入两种方式,并且两种方式都支持配置文件和配置项的管理。通过disconf-spring-boot-web可以感受简单的使用示例,只需要修改配置文件disconf.properties中的conf_server_host为上面部署的nginx地址然后运行,成功获取到配置中心的配置后,在disconf-web上对应的配置实例列表可以看到。

由于是系统的改造过程,所以并不适合采用demo中的直接在JavaBean的get方法上注解每个配置项的方法。为了适应原有系统的逻辑,我们需要的是配置文件的统一管理,同时又能让配置及时生效。因此结合disconf的特性和原有系统的特性,选择用基于注解的配置类和回调类实现配置文件的管理。首先在pom文件中增加disconf-client的依赖,然后在工程中增加WebApiConfig和WebApiConfigCallback两个类:

@Service
@DisconfFile(filename = "web-api.xml")
public class WebApiConfig {

}

@DisconfUpdateService(classes = { WebApiConfig.class}, confFileKeys = {"testJson.json"})
public class WebApiConfigCallback implements IDisconfUpdate {

    public void reload() throws Exception {
        System.out.println("config update callback, do reload here"); // 回调重载配置
    }

}

对于基于springmvc的web工程,由于disconf必须在最先启动,所以在web.xml的最前面增加如下配置:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:disconf.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

同时在classpath下增加disconf.properties和disconf.xml两个配置文件。具体配置这里就不贴了,看demo就知道了。最后启动应用时(别忘了在log4j的配置中增加com.baidu的log)就可以看到应用会先从配置中心下载web-api.xml文件到classpath,然后开始初始化进程。接着我们来测试回调的使用,在disconf页面上找到对应的配置文件web-api.xml,修改它的内容就可以看到所有使用该配置的服务触发了回调方法。

上面的使用都是基于spring的应用,如果原工程是非spring的怎么办呢?很简单,手动载入disconf即可。在main方法的最前面调用如下方法:

// 初始化disconf
private static void contextInitialized() {
    String[] fn = new String[] {"disconf.xml"};
    new ClassPathXmlApplicationContext(fn);
}

总结

配置中心的部署实现相对比较简单,但是它是微服务化过程中不可缺少的一部分。在实践过程中我们需要考虑怎么使用配置中心才能最合理的适配原有的系统,哪些配置文件和配置项适合用或应该用配置中心管理,disconf的功能能否满足系统的需求,是否需要做二次开发等等,这些问题都应该充分考虑到。同时在学习使用disconf的过程中,可以想想有哪些优秀的架构设计可以学习借鉴。