分佈式配置管理平臺Disconf

摘要

爲了更好的解決分佈式環境下多臺服務實例的配置統一管理問題,本文提出了一套完整的分佈式配置管理解決方案(簡稱爲disconf[4],下同)。首先,實現了同構系統的配置發佈統一化,提供了配置服務server,該服務能夠對配置進行持久化管理並對外提供restful接口,在此基礎上,基於zookeeper實現對配置更改的實時推送,而且,提供了穩定有效的容災方案,以及用戶體驗良好的編程模型和WEB用戶管理界面。其次,實現了異構系統的配置包管理,提出基於zookeeper的全局分佈式一致性鎖來實現主備統一部署、系統異常時的主備自主切換。經過在百度內部以及外部等多個產品線的實踐結果代表,本解決方案是有效且穩定的。html

技術背景

在一個分佈式環境中,同類型的服務每每會部署不少實例。這些實例使用了一些配置,爲了更好地維護這些配置就產生了配置管理服務。經過這個服務能夠輕鬆地管理成千上百個服務實例的配置問題。mysql

王阿晶提出了基於zooKeeper的配置信息存儲方案的設計與實現[1], 它將全部配置存儲在zookeeper上,這會致使配置的管理不那麼方便,並且他們沒有相關的源碼實現。淘寶的diamond[2]是淘寶內部使用的一個管理持久配置的系統,它具備完整的開源源碼實現,它的特色是簡單、可靠、易用,淘寶內部絕大多數系統的配置都採用diamond來進行統一管理。他將全部配置文件裏的配置打散化進行存儲,只支持KV結構,而且配置更新的推送是非實時的。百度內部的BJF配置中心服務[3]採用了相似淘寶diamond的實現,也是配置打散化、只支持KV和非實時推送。git

同構系統是市場的主流,特別地,在業界大量使用部署虛擬化(如JPAAS系統,SAE,BAE)的狀況下,同一個系統使用同一個部署包的情景會愈來愈多。可是,異構系統也有必定的存在乎義,譬如,對於「拉模式」的多個下游實例,同一時間點只能只有一個下游實例在運行。在這種情景下,就存在多臺實例機器有「主備機」模式的問題。目前國內並無很明顯的解決方案來統一解決此問題。程序員

功能特色與設計理念

disconf是一套完整的基於zookeeper的分佈式配置統一解決方案。github

它的功能特色是web

  • 支持配置(配置項+配置文件)的分佈式化管理
    • 配置發佈統一化
    • 配置發佈、更新統一化(雲端存儲、發佈):配置存儲在雲端系統,用戶統一在平臺上進行發佈、更新配置。
    • 配置更新自動化:用戶在平臺更新配置,使用該配置的系統會自動發現該狀況,並應用新配置。特殊地,若是用戶爲此配置定義了回調函數類,則此函數類會被自動調用。
  • 配置異構系統管理
    • 異構包部署統一化:這裏的異構系統是指一個系統部署多個實例時,因爲配置不一樣,從而須要多個部署包(jar或war)的狀況(下同)。使用Disconf後,異構系統的部署只須要一個部署包,不一樣實例的配置會自動分配。特別地,在業界大量使用部署虛擬化(如JPAAS系統,SAE,BAE)的狀況下,同一個系統使用同一個部署包的情景會愈來愈多,Disconf能夠很天然地與他自然契合。 異構主備自動切換:若是一個異構系統存在主備機,主機發生掛機時,備機能夠自動獲取主機配置從而變成主機。
    • 異構主備機Context共享工具:異構系統下,主備機切換時可能須要共享Context。可使用Context共享工具來共享主備的Context。
  • 註解式編程,極簡的使用方式:咱們追求的是極簡的、用戶編程體驗良好的編程方式。經過簡單的標註+極簡單的代碼撰寫,便可完成複雜的配置分佈式化。
  • 須要Spring編程環境

它的設計理念是:redis

  • 簡單,用戶體驗良好:
    • 摒棄了打散化配置的管理方式[2,3],仍舊採用基於配置文件的編程方式,這和程序員之前的編程習慣(配置都是放在配置文件裏)一致。特別的,爲了支持較爲小衆的打散化配置功能,還特別支持了配置項。
    • 採用了基於XML無代碼侵入編程方式:只須要幾行XML配置,便可實現配置文件發佈更新統一化、自動化。
    • 採用了基於註解式的弱代碼侵入編程方式:經過編程規範,一個配置文件一個配置類,代碼結構簡單易懂。XML幾乎沒有任何更改,與原springXML配置同樣。真正編程時,幾乎感受不到配置已經分佈式化
  • 能夠託管任何類型的配置文件,這與[2,3]只能支持KV結構的功能有較大的改進。
  • 配置更新實時推送
  • 提供界面良好Web管理功能,能夠很是方便的查看配置被哪些實例使用了。

詳細設計

架構設計

disconf服務集羣模式spring

disconf的模塊架構圖sql

每一個模塊的簡單介紹以下:數據庫

  • Disconf-core
    • 分佈式通知模塊:支持配置更新的實時化通知
    • 路徑管理模塊:統一管理內部配置路徑URL
  • Disconf-client
    • 配置倉庫容器模塊:統一管理用戶實例中本地配置文件和配置項的內存數據存儲
    • 配置reload模塊:監控本地配置文件的變更,並自動reload到指定bean
    • 掃描模塊:支持掃描全部disconf註解的類和域
    • 下載模塊:restful風格的下載配置文件和配置項
    • watch模塊:監控遠程配置文件和配置項的變化
    • 主備分配模塊:主備競爭結束後,統一管理主備分配與主備監控控制
    • 主備競爭模塊:支持分佈式環境下的主備競爭
  • Disconf-web
    • 配置存儲模塊:管理全部配置的存儲和讀取
    • 配置管理模塊:支持配置的上傳、下載、更新
    • 通知模塊:當配置更新後,實時通知使用這些配置的全部實例
    • 配置自檢監控模塊:自動定時校驗實例本地配置與中心配置是否一致
    • 權限控制:web的簡單權限控制
  • Disconf-tools
    • context共享模塊:提供多實例間context的共享。

流程設計

運行流程詳細介紹:

與2.0版本的主要區別是支持了:主備分配功能/主備切換事件。

  • 啓動事件A:如下按順序發生。
    • A3:掃描靜態註解類數據,並注入到配置倉庫裏。
    • A4+A2:根據倉庫裏的配置文件、配置項,去 disconf-web 平臺裏下載配置數據。這裏會有主備競爭
    • A5:將下載獲得的配置數據值注入到倉庫裏。
    • A6:根據倉庫裏的配置文件、配置項,去ZK上監控結點。
    • A7+A2:根據XML配置定義,到 disconf-web 平臺裏下載配置文件,放在倉庫裏,並監控ZK結點。這裏會有主備競爭。
    • A8:A1-A6均是處理靜態類數據。A7是處理動態類數據,包括:實例化配置的回調函數類;將配置的值注入到配置實體裏。
  • 更新配置事件B:如下按順序發生。
    • B1:管理員在 Disconf-web 平臺上更新配置。
    • B2:Disconf-web 平臺發送配置更新消息給ZK指定的結點。
    • B3:ZK通知 Disconf-cient 模塊。
    • B4:與A4同樣。
    • B5:與A5同樣。
    • B6:基本與A4同樣,惟一的區別是,這裏還會將配置的新值注入到配置實體裏。
  • 主備機切換事件C:如下按順序發生。
    • C1:發生主機掛機事件。
    • C2:ZK通知全部被影響到的備機。
    • C4:與A2同樣。
    • C5:與A4同樣。
    • C6:與A5同樣。
    • C7:與A6同樣。

模塊實現

disconf-web提供了先後端分離的web架構,具體可見:https://github.com/knightliao/disconf/tree/master/disconf-web

本部分會重點介紹disconf-client的實現方式。

註解式disconf實現

本實現會涉及到 配置倉庫容器模塊、掃描模塊、下載模塊、watch模塊,

http://ww1.sinaimg.cn/bmiddle/60c9620fjw1eqj9zzgc7yj20b20pn41v.jpg

使用AOP攔截的一個好處是能夠比較輕鬆的實現配置控制,好比並發環境下的配置統一輩子效。關於這方面的討論能夠見這裏

特別地,本方式提供的編程模式很是簡單,例如使用如下配置類的程序在使用它時,能夠直接@Autowired進來進行調用,使用它時就和日常使用普通的JavaBean同樣,但其實它已經分佈式化了。配置更新時,配置類亦會自動更新。

@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實現

本實現提供了無任何代碼侵入方式的分佈式配置。

ReloadablePropertiesFactoryBean繼承了Spring Properties文件的PropertiesFactoryBean類,管理全部當配置更新時要進行reload的配置文件。對於被管理的每個配置文件,都會經過 配置倉庫容器模塊、掃描模塊、下載模塊、watch模塊 進行配置獲取至配置倉庫裏。

ReloadingPropertyPlaceholderConfigurer繼承了Spring Bean配置值控制類PropertyPlaceholderConfigurer。在第一次掃描spring bean裏,disconf會記錄配置文件的配置與哪些bean有關聯。

ReloadConfigurationMonitor是一個定時任務,定時check本地配置文件是否有更新。

當配置中心的配置被更新時,配置文件會被下載至實例本地,ReloadConfigurationMonitor即會監控到此行爲,而且通知 ReloadingPropertyPlaceholderConfigurer 對相關的bean類進行值更新。

特別的,此種方式沒法解決併發狀況下配置統一輩子效的問題。

主備分配實現

在實現中,爲每一個配置提供主備選擇的概念。用戶實例在獲取配置前須要先進行全局惟一性競爭才能獲得配置值。在這裏,咱們採用基於zookeeper的全局惟一性鎖來實現。

Comparisons

  淘寶Diamond[2] Disconf 比較
數據持久性 存儲在mysql上 存儲在mysql上 都持久化到數據庫裏,都易於管理
推拉模型 拉模型,每隔15s拉一次全量數據 基於Zookeeper的推模型,實時推送 disconf基於分佈式的Zookeeper來實時推送,在穩定性、實效性、易用性上均優於diamond
配置讀寫 支持實例對配置讀寫。支持某臺實例寫配置數據,並廣播到其它實例上 只支持實例對配置讀。經過在disconf-web上更新配置到達到廣播寫到全部應用實例 從目前的應用場景來看,實例對配置的寫需求不是那麼明顯。disconf支持的中心化廣播方案可能會與人性思考更加類似。
容災 多級容災模式,配置數據會dump在本地,避免中心服務掛機時沒法使用 多級容災模式,優先讀取本地配置文件。 雙方均支持在中心服務掛機時配置實例仍然可使用
配置數據模型 只支持KV結構的數據,非配置文件模式 支持傳統的配置文件模式(配置文件),亦支持KV結構數據(配置項) 使用配置文件的編程方式可能與程序員的編程習慣更爲類似,更易於接受和使用。
編程模型 須要將配置文件拆成多個配置項,沒有明顯的編程模型 在使用配置文件的基礎上,提供了註解式和基於XML的兩種編程模型
併發性 多條配置要同時生效時,沒法解決併發同時生效的問題 基於註解式的配置,能夠解決併發性問題

Reference

  1. 王阿晶,鄒仕洪: 基於ZooKeeper的配置信息存儲方案的設計與實現
  2. 淘寶diamod實現:http://code.taobao.org/p/diamond/src/, 2012
  3. 百度BJF配置中心, 2014
  4. disconf github: https://github.com/knightliao/disconf, 2014
  5. 淘寶分佈式配置管理服務Diamond
  6. zooKeeper和Diamond有什麼不一樣
  7. diamond專題(一)-- 簡介和快速使用
相關文章
相關標籤/搜索