Disconf實現(xiàn)分布式配置管理的原理與設計
技術(shù)背景
在一個分布式環(huán)境中,同類型的服務往往會部署很多實例。這些實例使用了一些配置,為了更好地維護這些配置就產(chǎn)生了配置管理服務。通過這個服務可以輕松地管理成千上百個服務實例的配置問題。
王阿晶提出了基于zooKeeper的配置信息存儲方案的設計與實現(xiàn)[1], 它將所有配置存儲在zookeeper上,這會導致配置的管理不那么方便,而且他們沒有相關的源碼實現(xiàn)。淘寶的diamond[2]是淘寶內(nèi)部使用的一個管理持久配置的系統(tǒng),它具有完整的開源源碼實現(xiàn),它的特點是簡單、可靠、易用,淘寶內(nèi)部絕大多數(shù)系統(tǒng)的配置都采用diamond來進行統(tǒng)一管理。他將所有配置文件里的配置打散化進行存儲,只支持KV結(jié)構(gòu),并且配置更新的推送是非實時的。百度內(nèi)部的BJF配置中心服務[3]采用了類似淘寶diamond的實現(xiàn),也是配置打散化、只支持KV和非實時推送。
同構(gòu)系統(tǒng)是市場的主流,特別地,在業(yè)界大量使用部署虛擬化(如JPAAS系統(tǒng),SAE,BAE)的情況下,同一個系統(tǒng)使用同一個部署包的情景會越來越多。但是,異構(gòu)系統(tǒng)也有一定的存在意義,譬如,對于“拉模式”的多個下游實例,同一時間點只能只有一個下游實例在運行。在這種情景下,就存在多臺實例機器有“主備機”模式的問題。目前國內(nèi)并沒有很明顯的解決方案來統(tǒng)一解決此問題。
功能特點與設計理念
disconf是一套完整的基于zookeeper的分布式配置統(tǒng)一解決方案。
功能特點
支持配置(配置項+配置文件)的分布式化管理
- 配置發(fā)布統(tǒng)一化
- 配置發(fā)布、更新統(tǒng)一化(云端存儲、發(fā)布):配置存儲在云端系統(tǒng),用戶統(tǒng)一在平臺上進行發(fā)布、更新配置。
- 配置更新自動化:用戶在平臺更新配置,使用該配置的系統(tǒng)會自動發(fā)現(xiàn)該情況,并應用新配置。特殊地,如果用戶為此配置定義了回調(diào)函數(shù)類,則此函數(shù)類會被自動調(diào)用。
配置異構(gòu)系統(tǒng)管理
- 異構(gòu)包部署統(tǒng)一化:這里的異構(gòu)系統(tǒng)是指一個系統(tǒng)部署多個實例時,由于配置不同,從而需要多個部署包(jar或war)的情況(下同)。使用Disconf后,異構(gòu)系統(tǒng)的部署只需要一個部署包,不同實例的配置會自動分配。特別地,在業(yè)界大量使用部署虛擬化(如JPAAS系統(tǒng),SAE,BAE)的情況下,同一個系統(tǒng)使用同一個部署包的情景會越來越多,Disconf可以很自然地與他天然契合。 異構(gòu)主備自動切換:如果一個異構(gòu)系統(tǒng)存在主備機,主機發(fā)生掛機時,備機可以自動獲取主機配置從而變成主機。
- 異構(gòu)主備機Context共享工具:異構(gòu)系統(tǒng)下,主備機切換時可能需要共享Context。可以使用Context共享工具來共享主備的Context。
注解式編程,極簡的使用方式:我們追求的是極簡的、用戶編程體驗良好的編程方式。通過簡單的標注+極簡單的代碼撰寫,即可完成復雜的配置分布式化。
需要Spring編程環(huán)境
設計理念
簡單,用戶體驗良好:
- 摒棄了打散化配置的管理方式[2,3],仍舊采用基于配置文件的編程方式,這和程序員以前的編程習慣(配置都是放在配置文件里)一致。特別的,為了支持較為小眾的打散化配置功能,還特別支持了配置項。
- 采用了基于XML無代碼侵入編程方式:只需要幾行XML配置,即可實現(xiàn)配置文件發(fā)布更新統(tǒng)一化、自動化。
- 采用了基于注解式的弱代碼侵入編程方式:通過編程規(guī)范,一個配置文件一個配置類,代碼結(jié)構(gòu)簡單易懂。XML幾乎沒有任何更改,與原springXML配置一樣。真正編程時,幾乎感覺不到配置已經(jīng)分布式化
可以托管任何類型的配置文件,這與[2,3]只能支持KV結(jié)構(gòu)的功能有較大的改進。
配置更新實時推送
提供界面良好Web管理功能,可以非常方便的查看配置被哪些實例使用了。
詳細設計
架構(gòu)設計
disconf服務集群模式:

disconf的模塊架構(gòu)圖:

每個模塊的簡單介紹如下:
- Disconf-core
- 分布式通知模塊:支持配置更新的實時化通知
- 路徑管理模塊:統(tǒng)一管理內(nèi)部配置路徑URL
- Disconf-client
- 配置倉庫容器模塊:統(tǒng)一管理用戶實例中本地配置文件和配置項的內(nèi)存數(shù)據(jù)存儲
- 配置reload模塊:監(jiān)控本地配置文件的變動,并自動reload到指定bean
- 掃描模塊:支持掃描所有disconf注解的類和域
- 下載模塊:restful風格的下載配置文件和配置項
- watch模塊:監(jiān)控遠程配置文件和配置項的變化
- 主備分配模塊:主備競爭結(jié)束后,統(tǒng)一管理主備分配與主備監(jiān)控控制
- 主備競爭模塊:支持分布式環(huán)境下的主備競爭
- Disconf-web
- 配置存儲模塊:管理所有配置的存儲和讀取
- 配置管理模塊:支持配置的上傳、下載、更新
- 通知模塊:當配置更新后,實時通知使用這些配置的所有實例
- 配置自檢監(jiān)控模塊:自動定時校驗實例本地配置與中心配置是否一致
- 權(quán)限控制:web的簡單權(quán)限控制
- Disconf-tools
- context共享模塊:提供多實例間context的共享。
流程設計

運行流程詳細介紹:
與2.0版本的主要區(qū)別是支持了:主備分配功能/主備切換事件。
- 啟動事件A:以下按順序發(fā)生。
- A3:掃描靜態(tài)注解類數(shù)據(jù),并注入到配置倉庫里。
- A4+A2:根據(jù)倉庫里的配置文件、配置項,去 disconf-web 平臺里下載配置數(shù)據(jù)。這里會有主備競爭
- A5:將下載得到的配置數(shù)據(jù)值注入到倉庫里。
- A6:根據(jù)倉庫里的配置文件、配置項,去ZK上監(jiān)控結(jié)點。
- A7+A2:根據(jù)XML配置定義,到 disconf-web 平臺里下載配置文件,放在倉庫里,并監(jiān)控ZK結(jié)點。這里會有主備競爭。
- A8:A1-A6均是處理靜態(tài)類數(shù)據(jù)。A7是處理動態(tài)類數(shù)據(jù),包括:實例化配置的回調(diào)函數(shù)類;將配置的值注入到配置實體里。
- 更新配置事件B:以下按順序發(fā)生。
- B1:管理員在 Disconf-web 平臺上更新配置。
- B2:Disconf-web 平臺發(fā)送配置更新消息給ZK指定的結(jié)點。
- B3:ZK通知 Disconf-cient 模塊。
- B4:與A4一樣。
- B5:與A5一樣。
- B6:基本與A4一樣,唯一的區(qū)別是,這里還會將配置的新值注入到配置實體里。
- 主備機切換事件C:以下按順序發(fā)生。
- C1:發(fā)生主機掛機事件。
- C2:ZK通知所有被影響到的備機。
- C4:與A2一樣。
- C5:與A4一樣。
- C6:與A5一樣。
- C7:與A6一樣。
模塊實現(xiàn)
disconf-web提供了前后端分離的web架構(gòu),具體可見:https://github.com/knightliao/disconf/tree/master/disconf-web
本部分會重點介紹disconf-client的實現(xiàn)方式。
注解式disconf實現(xiàn)
本實現(xiàn)會涉及到 配置倉庫容器模塊、掃描模塊、下載模塊、watch模塊,

使用AOP攔截的一個好處是可以比較輕松的實現(xiàn)配置控制,比如并發(fā)環(huán)境下的配置統(tǒng)一生效。
特別地,本方式提供的編程模式非常簡單,例如使用以下配置類的程序在使用它時,可以直接@Autowired進來進行調(diào)用,使用它時就和平常使用普通的JavaBean一樣,但其實它已經(jīng)分布式化了。配置更新時,配置類亦會自動更新。
@Service
@DisconfFile(filename = "redis.properties")
public class JedisConfig {
// 代表連接地址
private String host;
// 代表連接port
private int port;
/**
* 地址, 分布式文件配置
*
* @return
*/
@DisconfFileItem(name = "redis.host", associateField = "host")
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
/**
* 端口, 分布式文件配置
*
* @return
*/
@DisconfFileItem(name = "redis.port", associateField = "port")
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}基于XML配置disconf實現(xiàn)
本實現(xiàn)提供了無任何代碼侵入方式的分布式配置。
ReloadablePropertiesFactoryBean繼承了Spring Properties文件的PropertiesFactoryBean類,管理所有當配置更新時要進行reload的配置文件。對于被管理的每一個配置文件,都會通過 配置倉庫容器模塊、掃描模塊、下載模塊、watch模塊 進行配置獲取至配置倉庫里。
ReloadingPropertyPlaceholderConfigurer繼承了Spring Bean配置值控制類PropertyPlaceholderConfigurer。在第一次掃描spring bean 時,disconf會記錄配置文件的配置與哪些bean有關聯(lián)。
ReloadConfigurationMonitor是一個定時任務,定時check本地配置文件是否有更新。
當配置中心的配置被更新時,配置文件會被下載至實例本地,ReloadConfigurationMonitor即會監(jiān)控到此行為,并且通知 ReloadingPropertyPlaceholderConfigurer 對相關的bean類進行值更新。
特別的,此種方式無法解決并發(fā)情況下配置統(tǒng)一生效的問題。
主備分配實現(xiàn)
在實現(xiàn)中,為每個配置提供主備選擇的概念。用戶實例在獲取配置前需要先進行全局唯一性競爭才能得到配置值。在這里,我們采用基于zookeeper的全局唯一性鎖來實現(xiàn)。
COMPARISONS
| 淘寶Diamond[2] | Disconf | 比較 | |
|---|---|---|---|
| 數(shù)據(jù)持久性 | 存儲在mysql上 | 存儲在mysql上 | 都持久化到數(shù)據(jù)庫里,都易于管理 |
| 推拉模型 | 拉模型,每隔15s拉一次全量數(shù)據(jù) | 基于Zookeeper的推模型,實時推送 | disconf基于分布式的Zookeeper來實時推送,不斷是在穩(wěn)定性、實效性、易用性上均優(yōu)于diamond |
| 配置讀寫 | 支持實例對配置讀寫。支持某臺實例寫配置數(shù)據(jù),并廣播到其它實例上 | 只支持實例對配置讀。通過在disconf-web上更新配置到達到廣播寫到所有應用實例 | 從目前的應用場景來看,實例對配置的寫需求不是那么明顯。disconf支持的中心化廣播方案可能會與人性思考更加相似。 |
| 容災 | 多級容災模式,配置數(shù)據(jù)會dump在本地,避免中心服務掛機時無法使用 | 多級容災模式,優(yōu)先讀取本地配置文件。 | 雙方均支持在中心服務掛機時配置實例仍然可以使用 |
| 配置數(shù)據(jù)模型 | 只支持KV結(jié)構(gòu)的數(shù)據(jù),非配置文件模式 | 支持傳統(tǒng)的配置文件模式(配置文件),亦支持KV結(jié)構(gòu)數(shù)據(jù)(配置項) | 使用配置文件的編程方式可能與程序員的編程習慣更為相似,更易于接受和使用。 |
| 編程模型 | 需要將配置文件拆成多個配置項,沒有明顯的編程模型 | 在使用配置文件的基礎上,提供了注解式和基于XML的兩種編程模型 | 無 |
| 并發(fā)性 | 多條配置要同時生效時,無法解決并發(fā)同時生效的問題 | 基于注解式的配置,可以解決并發(fā)性問題 | 無 |
以上就是Disconf實現(xiàn)分布式配置管理的原理與設計的詳細內(nèi)容,更多關于Disconf分布式配置管理的資料請關注腳本之家其它相關文章!
相關文章
Executor攔截器高級教程QueryInterceptor的規(guī)范
今天小編就為大家分享一篇關于Executor攔截器高級教程QueryInterceptor的規(guī)范,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12
使用concurrentHashMap如何實現(xiàn)緩存
文章介紹了使用ConcurrentHashMap實現(xiàn)緩存的線程安全性和初始化方法,以及如何處理高并發(fā)場景下的緩存清理和數(shù)據(jù)一致性問題,包括分桶、使用ConcurrentLinkedQueue以及使用CountDownLatch來確保緩存數(shù)據(jù)的不丟失2025-02-02
Spring Boot與Kotlin處理Web表單提交的方法
本篇文章主要介紹了Spring Boot 與 Kotlin 處理Web表單提交的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01
Spring Boot2配置Swagger2生成API接口文檔詳情
這篇文章主要介紹了Spring Boot2配置Swagger2生成API接口文檔詳情,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09
springboot中如何配置LocalDateTime JSON返回時間戳
這篇文章主要介紹了springboot中如何配置LocalDateTime JSON返回時間戳問題。具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06

