程序员过关斩将--为微服务撸一个简约而不简单的配置中心
點擊上方藍字 ?關注我們
毫不猶豫的說,現代高速發展的互聯網造就了一批又一批的網絡紅人,這一批批網紅又極大的催生了特定平臺的一大波流量,但是留給了程序員卻是一地雞毛,無論是運維還是開發,每天都會擔心服務器崩潰,程序down機。還是懷念以前那些單機結構呀,甚至有點嫉妒那些做內網幾乎沒有訪問量的應用的程序員,不用加班,不用提心吊膽,更不用每年買霸王洗發露。
??
提到單機架構,在互聯網應用中肯定是吃不開的,流量高峰沖擊的你可以懷疑人生。單機升級集群,帶來的不止是技術上的挑戰,在頂住流量高峰,迎合業務的同時,也引入了配置的復雜性。這也是我今天要談的主題:配置管理。在單機時代,無論是什么語言,java也好,c#也罷,一個配置文件足以。隨著所謂微服務這個看似能解決一切問題的方案誕生,同時也引入了更加復雜的配置問題:服務的信息,服務的各種參數,配置更新問題等。可想而知,假如你的服務有100臺服務器,修改一個配置項,利用單體架構逐個更新的方式是一個多么蛋疼的事情,傳統的配置文件方式已經無法滿足開發人員對于配置管理的要求:
安全性。配置信息如果隨代碼一起發布,容易造成配置泄露。
實時性。修改配置,傳統的單機架構必須重啟服務才能生效。
局限性。無法支持動態調整,像最普通的日志開關功能,也不能做到。
環境區分。傳統的配置文件方式,很難區分生產,開發,測試環境。
配置修改記錄問題。靜態配置文件方式,很難追蹤這個配置文件的修改記錄。
針對以上問題,有的公司采用數據庫記錄配置來解決問題,不是說不可以,只不過數據庫并不能解決根本性問題,舉個很簡單的例子:有最新的記錄修改,客戶端怎么能實時得到通知呢?雖然可以利用其他方案來解決,但是基于數據庫方式并非是最優的。
??
配置中心無論是采用數據庫方式也好,還是采用其他方式也罷,最本質的出發點還是要看業務具體需求和相應的可以投入的人力物力。不是說像攜程的apollo不好,而是說這樣一套龐大的配置系統是否適用于你的公司,是否適用于你的業務。在很多情況下,公司的業務發展在一定階段講究的是短平快,沒有必要也沒有時間去投入精力去深研究一套龐大的系統,在業務慢慢發展起來之后可以慢慢迭代,這才是系統架構升級的過程,那些業務之初就要建立淘寶級架構的,最終會落得人人疲憊。
說了這么多,我就是想擼一套簡約的,可落地的配置中心,要保證配置中心能正常工作,有幾點是在設計之初要考慮的:
需要一個可靠的,強一致性的存儲來支撐
在經過了多次技術調研之后,我最終選擇了ETCD,并非因為我喜歡最求熱點,而是ETCD在應對場景,功能上恰好對應我的需求,
?etcd是CoreOS團隊于2013年6月發起的開源項目,它的目標是構建一個高可用的分布式鍵值(key-value)數據庫。etcd內部采用raft協議作為一致性算法,etcd基于Go語言實現。
?簡單:安裝配置簡單,而且提供了HTTP API進行交互,使用也很簡單
安全:支持SSL證書驗證
快速:根據官方提供的benchmark數據,單實例支持每秒2k+讀操作
可靠:采用raft算法,實現分布式系統數據的可用性和一致性
Watch機制:ETCD支持針對某個Key或者某組Key的Watch機制,一旦有數據發生變化,會實時通知Client。
需要支持多環境的配置
雖然很多存儲的顯示參數都沒有環境這樣的參數,但是都提供了類似目錄存儲這樣的功能,就像windows的文件目錄一樣,這就為我們自定義功能提供了很強的應變能力,例如,我們存儲A應用開發環境可以是這樣的:/A/DEV/ ,這個目錄就代表了A應用的開發環境配置。
配置項發生變化,需要實時通知客戶端
基于第一點選擇的ETCD天生就支持Watch機制,所以配置項發生變化實時通知客戶端這點是很好做到的,就算了通知失敗,我們也可以自定義時間來延遲更新配置。稍后請看以下代碼。
使用要簡單
對于使用者來說,配置中心提供的業務接口最終只有:獲取某個key的配置,這里的key可以是應用+環境等參數的合集。為了輔助跟蹤,可以暴露出程序異常時候的處理事件,就像以下程序:
/// <summary>/// 配置中心客戶端,應用,子應用,環境、版本、灰度、/// </summary>public interface IConfigCenterClient{/// <summary>/// 配置信息發生變化時候的事件,參數:key/new value /動作(put /delete), 是etcd /consul watch的事件。每個key 的value發生變化都會觸發,每個key會觸發一條/// </summary>event Action<string, string, string> ConfigValueChangedEvent;/// <summary>/// 發生異常時候的事件/// </summary>event Action<Exception> ErrorOccurredEvent;/// <summary>/// 獲取相應的配置/// </summary>/// <param name="configKey">配置的名稱</param>/// <param name="version">版本號</param>/// <returns></returns>string GetConfig(string configKey);}抽象出這個操作接口之后,至于具體怎么樣實現,可以是基于ETCD,可以是基于Consul,甚至可以基于DB,可見面向接口編程是多么的正確。
在網絡故障等情況下需要能繼續工作
在互聯網應用中,始終存在一個真理:網絡是不可靠的。配置中心作為公司的一個核心系統來說,要盡可能的保證能提供服務。但是,也要做好了預防措施,以防在配置中心短暫不可用的期間,不影響適用方。對于使用client端來說,既然服務端保證不了高可用,那就需要在本地動手腳:可以把獲取到的配置信息在本地做存儲,信息并隨著watch機制做持久化。這樣在配置中心網絡不可用的情況下,盡量保證客戶端程序可用。至于本地存儲的方式,無所謂了,就算了文本文件,還是sqllite都可以。
性能要高
配置中心最顯著的一個業務特點是變化不頻繁,但是客戶端使用頻繁。所以我們可以把配置信息加載在內存中,內存中的數據隨著watch機制改變而改變,這樣就做到了內存數據和服務端數據高度一致。
以上啰嗦了那么多,相信你們也看累了,空談理論誰都會,落地代碼才是真理。
客戶端配置中心接口
/// <summary>/// 配置中心客戶端,應用,子應用,環境、版本、灰度、/// </summary>public interface IConfigCenterClient{/// <summary>/// 配置信息發生變化時候的事件,參數:key/new value /動作(put /delete), 是etcd /consul watch的事件。每個key 的value發生變化都會觸發,每個key會觸發一條/// </summary>event Action<string, string, string> ConfigValueChangedEvent;/// <summary>/// 發生異常時候的事件/// </summary>event Action<Exception> ErrorOccurredEvent;/// <summary>/// 獲取相應的配置/// </summary>/// <param name="configKey">配置的名稱</param>/// <param name="version">版本號</param>/// <returns></returns>string GetConfig(string configKey);}/// <summary>/// 環境的定義/// </summary>public enum EnvEnum{/// <summary>/// 本地環境/// </summary>Local=1,/// <summary>/// 開發環境/// </summary>DEV,/// <summary>/// 仿真環境/// </summary>UAT,/// <summary>/// 生產環境/// </summary>PRO}public class ConfigCenterETCDProvider : ConfigCenterBaseProvider, IConfigCenterClient{//配置發生改變的通知事件public event Action<string, string, string> ConfigValueChangedEvent;//有異常發生的時候的事件public override event Action<Exception> ErrorOccurredEvent;//etcd的配置private ConfigCenterETCDOption option { get; set; }private EtcdClient client = null;internal ConfigCenterETCDProvider(ConfigCenterBaseOption _option) : base(_option){if (_option == null){throw new Exception("config is null!");}option = _option as ConfigCenterETCDOption;if (option == null){throw new Exception("option type is wrong!");}if (string.IsNullOrWhiteSpace(option.ConnectionString)){//默認地址option.ConnectionString = "http://127.0.0.1";}client = new EtcdClient(option.ConnectionString, option.Port, option.Username, option.Password, option.CaCert, option.ClientCert, option.ClientKey, option.PublicRootCa);client.WatchRange(WatchPath, ETCDWatchEvent, exceptionHandler: e =>{ErrorOccurredEvent?.Invoke(e);Thread.Sleep(1000);});//讀取本地文件只初始化讀取一次InitConfigMapFromLocal();}#region 實現的接口方法//設置某個配置的值public bool SetConfig(string configKey, string configValue){string key = FillConfigKey(configKey);try{var putRet = client.Put(key, configValue);}catch (Exception e){ErrorOccurredEvent?.Invoke(e);return false;}return true;}//刪除某個配置的值public bool DeleteConfig(string configKey){string key = FillConfigKey(configKey);try{client.Delete(key);}catch (Exception e){ErrorOccurredEvent?.Invoke(e);return false;}return true;}#endregion#region 基類 方法//etcd 組織keyprotected override string FillConfigKey(string configKey){return $"{WatchPath}/{configKey}";}//etcd 獲取遠程keyprotected override (bool isSuccess, string value) GetValueFromServer(string configKey){try{var value = client.GetVal(configKey);return (true, value);}catch (Exception e){ErrorOccurredEvent?.Invoke(e);return (false, null);}}#endregion#region 私有方法//監控value 變化的事件private void ETCDWatchEvent(WatchEvent[] response){lock (ConfigCenterBaseProvider.ObjLock){foreach (WatchEvent e in response){if (e.Type == Mvccpb.Event.Types.EventType.Delete){if (ConfigMap.ContainsKey(e.Key)){ConfigMap.Remove(e.Key);}}else{ConfigMap[e.Key] = e.Value;}Console.WriteLine(JsonConvert.SerializeObject(ConfigMap));if (ConfigValueChangedEvent != null){ConfigValueChangedEvent.Invoke(e.Key, e.Value, e.Type.ToString());}}if (response != null && response.Any()){//把最新的值寫會本地文件FlushLocalFileFromMap();}}}#endregion}鑒于代碼有點多,這里不再貼出,有興趣的小伙伴,可以添加菜菜微信或者公眾號"架構師修行之路"回復“配置中心”,基于ETCD完整的簡約版配置中心全部代碼奉上,最后來看張測試的圖
完
●程序員修神之路--為什么我會了SOA,你們還要逼我學微服務?
●程序員過關斬將--數據庫的樂觀鎖和悲觀鎖并非真實的鎖
●程序員修神之路--設計一套RPC框架并非易事
●程序員過關斬將--要想獲取我的用戶信息,就得按照規矩來
●程序員過關斬將--更加優雅的Token認證方式JWT
●程序員過關斬將--cookie和session的關系其實很簡單
●程序員修神之路--用NOSql給高并發系統加速
●程序員修神之路--高并發系統設計負載均衡架構
●程序員過關斬將--你為什么還在用存儲過程?
●程序員修神之路--問世間異步為何物?
●程序員修神之路--提高網站的吞吐
長按添加菜菜好友
關注后回復:“大禮包”和“福利”,領取驚喜
總結
以上是生活随笔為你收集整理的程序员过关斩将--为微服务撸一个简约而不简单的配置中心的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开发大会上,前微软CEO放出的狠话!.N
- 下一篇: Kubernetes的安全性怎么解?从4