以前个人2015下半年总结中有提到咱们的项目采用了微服务的模式,也就是说系统按必定的技术以及业务切分红各个独立的小系统,好比咱们的产品是一个电商系统,那么能够分为:前端WAP,前端api,商品管理系统,采购系统,主数据管理系统,用户中心管理,价格管理系统,促销管理系统,订单管理系统,库存管理系统,门店管理系统等等,最后统计的数据是dubbo服务就高达18个,web系统有3个,前端WAP站点一个。这些系统要想跑起来就须要链接各类资源,好比服务地址,数据库,缓存,文件系统,消息队列等,通常项目中使用到的配置项大体是以下两类:资源以及具体业务相关。html
配置中心的应用场景:前端
配置中心须要解决的核心问题是多个系统配置信息统一管理困难的问题,这里我关心的功能以下:git
这里贴一张百度的disconf图,这个项目的功能更增强大,有兴趣可去研究:github
首先咱们看下系统中是如何使用的配置项,通常有两种用法:web
a:某些XML配置文件中,好比:spring
<dubbo:protocol accesslog="true" name="dubbo" port="${zk.port}" />
b:程序中,通常是经过@Value这个注解来获取,好比咱们能够写一个配置类来加载配置项:数据库
@Service public class MmsConfig { @Value("${es.cluster.name}") private String esClusterName; public String getEsClusterName() { return esClusterName; }
这里的@Value的注解有两种用法:api
要搞清楚上面这两种用法,须要知道下面这几个类:缓存
搞清楚了系统从配置文件中取值的逻辑,那么理解统一配置中心就不难了,无非就是在加载配置项的地方作些手脚让其按照咱们的意图去获取更新配置项。这里咱们应用一个已经很是成熟的产品zookepper,它的数据结果相似以下:架构
核心的功能就是从zookepper中获取配置项而后加载到系统变量中便可。咱们看下若是将zookeeper中的配置项加载到系统中,根据PropertyPlaceholderConfigurer的功能描述,它会从三个地方去加载配置,咱们选择将zookeeper配置加载到系统变量中,核心代码以下两步:
private void setSystemProperys(ConfigCenter cc, Map<String, Object> config) { for(String key:config.keySet()){ String value=cc.get(key); if(key.contains(".")){ key=key.substring(1); } if(value==null) { value=""; } System.setProperty(key, value); } }
不一样环境的配置如何解决?
上面的功能只是提到了如何将zookepper中的配置加载到系统中,那么如何根据当前的环境加载正确的配置呢,这里也只须要在系统启动时传递一个环境变动便可,配置中心根据注入的环境变量值来判断应该加载哪一个环境的数据。若是是非web项目,咱们只须要在启动服务的命令中增长一个环境变动的参数便可:-Dmaven.test.skip=true clean install -Devn=sim,若是是web项目,咱们能够经过Servelt配置文件来完成,最终经过ServletContexstListener来获取参数,流程以下所示:
写一个自定义的ServletContextListener,它的做用主要是从系统启动环境中获取变量,提供给配置中心使用
public class WanmeiContextLoaderListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { String evn = System.getProperty("evn"); if(evn == null || evn.equals("")) { evn = sce.getServletContext().getInitParameter("evn"); if (evn == null) { evn = "qa"; } System.setProperty("evn", evn); } } @Override public void contextDestroyed(ServletContextEvent sce) { // TODO Auto-generated method stub } }
zookeeper中的配置项发生变化后如何更新bean中的值呢?
咱们能够利用guava提供的enventbus来解决,订阅一个zookeeper更新事件去更新系统变动便可,DataChangeEvent是自定义的一个类,要想实现自动更新须要写一些回调方法,也能够参考下这个项目:https://github.com/jamesmorgan/ReloadablePropertiesAnnotation
DataChangeEvent dataChangeEvent=new DataChangeEvent(map, DataChangeEvent.DataType.REMOTE, DataChangeEvent.ChangeType.DELETE); configOption.getEnventBus().post(dataChangeEvent);
如何配置呢?
只须要在PropertyPlaceholderConfigurer时加了一个depends-on就行,目的是让其先执行咱们的后门程序,其它的使用不受影响,基本不须要修改原有代码。
<bean id="initSpringProperties" class="config.center.spring.SpringPropertyInjectSupport" lazy-init="false" init-method="init"> <property name="configNameSpaces" value="/configcenter/mms" /> </bean> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="initSpringProperties"> <property name="locations"> <list> </list> </property> <property name="fileEncoding" value="UTF-8" /> </bean>
本文引用:
http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/
https://github.com/knightliao/disconf