以前個人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