新聞中心
微信研發(fā)體系下的分布式配置系統(tǒng)設(shè)計概要
作者:騰訊技術(shù)工程 2020-10-21 09:39:31
分布式 本文旨在分析分布式配置系統(tǒng)的必要性、可行性,及其關(guān)鍵約束,并介紹一款基于該系列分析,在微信研發(fā)體系下的實踐嘗試。

創(chuàng)新互聯(lián)公司堅信:善待客戶,將會成為終身客戶。我們能堅持多年,是因為我們一直可值得信賴。我們從不忽悠初訪客戶,我們用心做好本職工作,不忘初心,方得始終。十載網(wǎng)站建設(shè)經(jīng)驗創(chuàng)新互聯(lián)公司是成都老牌網(wǎng)站營銷服務(wù)商,為您提供網(wǎng)站制作、成都網(wǎng)站設(shè)計、網(wǎng)站設(shè)計、H5開發(fā)、網(wǎng)站制作、品牌網(wǎng)站制作、成都小程序開發(fā)服務(wù),給眾多知名企業(yè)提供過好品質(zhì)的建站服務(wù)。
作者:ypaapyyang,騰訊 WXG 后臺開發(fā)工程師,個人公眾號:碼農(nóng)課代表。
本文旨在分析分布式配置系統(tǒng)的必要性、可行性,及其關(guān)鍵約束,并介紹一款基于該系列分析,在微信研發(fā)體系下的實踐嘗試。
前言
對很多的業(yè)務(wù)開發(fā)同學而言,對運營素材的處理不是一件輕松的事,通常需要定制化的進行數(shù)據(jù)的清理、格式的轉(zhuǎn)換、工具的開發(fā)。筆者就曾過這樣一段不愉快的回憶,為了導(dǎo)入一次性的近十種類型的配置數(shù)據(jù),就耗去了兩天的時間。如果說這段經(jīng)歷有何價值的話,那就是促使我思考分布式配置系統(tǒng),并且在工作中實踐,使自己避免再次陷入如此槽糕的過程中。
本文正是旨在分析分布式配置系統(tǒng)的必要性、可行性,及其關(guān)鍵約束,并介紹一款基于該系列分析,在微信研發(fā)體系下的實踐嘗試。
配置的定義
我們清楚軟件建模的本質(zhì)是對現(xiàn)實世界(人、事、物及規(guī)則)的映射,映射的產(chǎn)出物即包括編程系統(tǒng)和配置。配置為我們提供了動態(tài)修改程序運行時行為的能力,即常說的“系統(tǒng)運行時飛行姿態(tài)的動態(tài)調(diào)整”,究其根源則是“我們?nèi)祟悷o法掌控和預(yù)知一切,映射到軟件領(lǐng)域上,我們總是需要對系統(tǒng)的某些功能特性預(yù)留出一些控制的線頭,以便我們在未來需要的時候,可以人為的撥弄這些線頭從而控制系統(tǒng)的行為特征?!?/p>
因此,本文所指的配置特指內(nèi)部運營人員產(chǎn)生的數(shù)據(jù)(廣義的系統(tǒng)運營人員,包括產(chǎn)品、運營、研發(fā)等),并且作為輸入?yún)?shù)而作用于編程系統(tǒng)(包括實時系統(tǒng)、批跑程序以及數(shù)據(jù)任務(wù)等)。
歸納而言,配置通常包含如下三種:
a. 環(huán)境配置,定義了應(yīng)用程序運行的環(huán)境相關(guān)參數(shù),如 IP、Port 等;
b. 應(yīng)用配置,定義了應(yīng)用程序自身相關(guān)的參數(shù)或者信息安全控制等,如初始內(nèi)存分配大小、數(shù)據(jù)庫連接池大小、日志級別、賬號密碼等;(密碼、證書這類東西肯定不要放在配置系統(tǒng)中,而應(yīng)當走統(tǒng)一加解密服務(wù))
c. 業(yè)務(wù)配置,定義了應(yīng)用程序所執(zhí)行的業(yè)務(wù)行為數(shù)據(jù),比如最常見的功能開關(guān),參與活動的商戶名單等。
系統(tǒng)約束
數(shù)據(jù)模型
配置最基本的數(shù)據(jù)單元是key=value(即配置項),比如功能開關(guān)通常就是最簡單的類型,用 boolean 型值來影響程序執(zhí)行鏈路(不考慮灰度的情況)。然而只有 key-value 類型是不足的,比如 DB 的連接配置就包含了 ip、port、username、password 等字段,在 ini 文件的實現(xiàn)中即是不同配置項來組成,它們在邏輯上是屬于同一個配置對象,因此基于面向?qū)ο蟮脑O(shè)計思路,key=object才是更通用的配置模型,在物理實現(xiàn)中可以為 json 或者 xml,或者 protobuf message。
object 類型的數(shù)據(jù)即可以是平坦的,也可以是多層次(嵌套)的。在實際的業(yè)務(wù)應(yīng)用中,平坦類型的數(shù)據(jù)有其特殊性,即其通常條目較多,最典型的數(shù)據(jù)是白名單,可能多達上萬條。線下,內(nèi)部運營人員通過excel進行這類數(shù)據(jù)的管理,如果我們只是粗暴的將其打包成一個對象,那么過大的數(shù)據(jù)可能會導(dǎo)致系統(tǒng)效率的下降(不是配置的寫入效率下降,就是配置讀出效率下降),因此我們會使用array of plain object來表達,即key=table類型的數(shù)據(jù)。
訪問模型
相別于產(chǎn)品用戶產(chǎn)生的數(shù)據(jù),配置系統(tǒng)的數(shù)據(jù)流是單向的,離線系統(tǒng)與實時系統(tǒng)結(jié)合而讀寫分離(異步寫、實時讀)的。最終我們要搭建的分布式配置系統(tǒng),它的系統(tǒng)設(shè)計,也必然是建立在這類訪問模型上的。
系統(tǒng)約束
顯然,內(nèi)部運營人員作為生產(chǎn)者,所有的配置肯定都是文本類型的(Readable),并且數(shù)據(jù)量少(相對于用戶、系統(tǒng)等生產(chǎn)數(shù)據(jù)而言),對存儲空間需求少,更新頻次低??梢赃@么理解,在整個配置系統(tǒng)架構(gòu)中,輸入方就如同鍵盤相對于 CPU 而言是超慢速設(shè)備,他們對系統(tǒng)的易用性、易操作性、安全性要求更高。
我們思考下用戶畫像系統(tǒng),它部分滿足配置系統(tǒng)的訪問模型,即數(shù)據(jù)流是單向的,離線系統(tǒng)負責寫入畫像數(shù)據(jù),而實時系統(tǒng)讀數(shù)據(jù)。但是首先它的數(shù)據(jù)生產(chǎn)者通常是離線任務(wù),而非運營人員;再次,它涉及到的數(shù)據(jù)量是巨大的,通常需要定制的存儲引擎。配置系統(tǒng)與之相比,不可同日而語。
相較而言,配置系統(tǒng)的消費者則是高頻的讀訪問,對系統(tǒng)的吞吐量、延時、網(wǎng)絡(luò)流量、可用性、一致性、請求單調(diào)性都有更高的要求。后續(xù)我們逐一展開深入的思考。
配置系統(tǒng)的設(shè)計應(yīng)當充分考慮到上述的數(shù)據(jù)模型、訪問模型以及系統(tǒng)約束。(比較奇怪的是,筆者在查閱相關(guān)配置系統(tǒng)實現(xiàn)時,鮮少看到有針對一致性、請求單調(diào)性的討論。這也是促使筆者撰寫本文的原因)
安全約束
正因為配置可以輕易的調(diào)整系統(tǒng)運行期行為,因此配置的安全性至關(guān)重要。實現(xiàn)安全的必要條件是:讓正確的人,以正確的方式,在正確的時機,發(fā)布正確的配置。因此,配置系統(tǒng)不但要支持灰度發(fā)布的基本能力,還要在權(quán)限管理、權(quán)限粒度管理、配置變更審核、審計、歷史版本等方面都要加強建設(shè)。
系統(tǒng)的演進
單機配置文件
在單機系統(tǒng)時代,我們基本上都是使用配置文件來存儲配置數(shù)據(jù)(比如 ini 文件、xml 文件等)。配置文件易于理解、便于實現(xiàn)、可用性高,因此進入分布式集群時代,仍在廣泛使用。
然而配置文件存在諸多的缺點,包括:
- 易用性差,主要體現(xiàn)為表達的數(shù)據(jù)類型單一,比如 ini 只能管理配置項,即 key=value 類型數(shù)據(jù);而如果使用 xml 文件來管理 key=table 類型數(shù)據(jù),那么文件內(nèi)容的初始化效率低下,容易出錯,難以維護;
- 可操作性差,配置文件基本只能由開發(fā)來進行修改并且發(fā)布,產(chǎn)品、運營的常規(guī)業(yè)務(wù)素材變更工作就不得不卷入開發(fā)執(zhí)行,對業(yè)務(wù)的流程效率有嚴重的影響;
- 正確性、安全性難以保障,正因為配置文件的易于實現(xiàn),導(dǎo)致很多團隊疏忽了運營系統(tǒng)的建設(shè),研發(fā)人員隨意修改、惡意修改配置文件的情況無法杜絕,細粒度的權(quán)限管理、操作的審核、審計無從談起;
- 發(fā)布效率低下,配置文件是單機部署的,在集群規(guī)模較大的情況下,配置文件的任意變更都需要經(jīng)過漫長的灰度發(fā)布過程發(fā)布到全網(wǎng),如果配置文件是靜態(tài)加載的,還需要重啟二進制,需要消耗研發(fā)、運維人員較多的精力;
- 文件一致性難以保障,在發(fā)布配置變更的過程中,如果集群中出現(xiàn)宕機情況,會導(dǎo)致不同機器間的配置出現(xiàn)差異,而沒有自動校正的能力,依賴于人員或者運維系統(tǒng)的支持,從而導(dǎo)致業(yè)務(wù)進入未定義的行為。
如果說易用性、可操作性、正確性、安全性可以通過搭建運營系統(tǒng)來進行改進,而發(fā)布效率低下、文件一致性難以保障則是單機配置文件的致命弱點,究其本質(zhì),是因為單機配置文件系統(tǒng)是被動的、離散的接受外界的變更,而沒有主動的能力。
集中式配置文件中心
由此,出現(xiàn)了集中式的配置文件系統(tǒng),針對性的解決了上述的問題,開發(fā)人員將配置文件存儲到獨立的第三方服務(wù)(典型的由 ZooKeeper 進行管理,也有部分團隊自行實現(xiàn)微服務(wù)管理),然后由 agent 周期性的將配置拉取到本地進行緩存(拉),或者通過事件的訂閱通知能力來將變更發(fā)布到相應(yīng)集群(推)。
集中式配置文件系統(tǒng)針對性的解決了發(fā)布變更效率問題以及配置文件一致性保障問題。然而在筆者所知的應(yīng)用案例中,仍然存在如下的問題亟需解決:
- 一致性粒度粗,集中式配置文件只能確保分布式集群達到最終一致(時間取決于拉、推的頻率及速率),卻無法保證任一時刻,對任一配置,所有進程、線程、協(xié)程看到相同的數(shù)據(jù),而這通常會導(dǎo)致出現(xiàn)不預(yù)期的業(yè)務(wù)失敗;
- 無法保證請求單調(diào)性,在一次業(yè)務(wù)請求中,我們希望用戶看到的配置內(nèi)容是靜態(tài)的,如果中間發(fā)生變更,可能帶來業(yè)務(wù)失敗,嚴重的導(dǎo)致用戶數(shù)據(jù)狀態(tài)錯亂;而基于集中式配置文件系統(tǒng)的配置通常是動態(tài)加載的,配置的變更可能隨時的反應(yīng)到實時系統(tǒng)中,導(dǎo)致一次業(yè)務(wù)請求先后看到不同的數(shù)據(jù)狀態(tài);
- 安全性仍無法徹底保障,雖然集中式配置文件的修改可以控制權(quán)限,但是在消費者機器上,開發(fā)者仍然可以手動的修改本地配置文件 cache 來影響程序的運行行為;
- 無法支持灰度能力,配置文件變更的下發(fā)是全量的,如果要支持灰度發(fā)布的能力,就需要卷入業(yè)務(wù)方自行實現(xiàn);
配置文件系統(tǒng),無論是單機配置文件,還是集中式配置文件,存在的問題,歸根結(jié)底,是由于配置文件這個載體以及集中式配置文件系統(tǒng)的管道定位決定的,從而導(dǎo)致進行精細化管理的成本高:
- 配置文件的可視可讀能力對生產(chǎn)者而言是重要的,但對消費者卻是無關(guān)緊要的,因此全鏈路都由配置文件作為載體反而可能導(dǎo)致加載效率低下(比如應(yīng)對千萬級黑白名單,或者業(yè)務(wù)方實時請求鏈路動態(tài)加載);
- 配置文件難以安全、便利管理元信息,為了實現(xiàn)一致性、單調(diào)性、安全性,配置需要一些元數(shù)據(jù)信息管理(下文展開詳述),但是配置文件系統(tǒng)沒有這種能力,除非業(yè)務(wù)方使用高成本自行實現(xiàn);
- 配置文件的數(shù)目與配置的數(shù)量息息相關(guān),隨著時間的發(fā)展,配置文件數(shù)目膨脹,帶來新的運營問題;
- 集中式配置文件系統(tǒng)通常只把自己定位成管道(據(jù)筆者所知),即不理解也不維護配置文件的內(nèi)容,agent 功能單一,業(yè)務(wù)消費方不與系統(tǒng)直接交互,而是只看到配置文件,雖然松耦合可以提高可用性,但也讓業(yè)務(wù)方仍然投入不少的開發(fā)成本來處理配置文件。
配置文件只是配置的物理載體,上述缺點并非無法克服,只是在基于配置文件的配置系統(tǒng)下,實現(xiàn)上述能力的成本高,需要更多的使用約束,以及外圍配套。
數(shù)據(jù)庫配置存儲
對結(jié)構(gòu)復(fù)雜、類型較多的配置,業(yè)務(wù)研發(fā)同學通常也不會直接使用配置文件來承載,而是使用數(shù)據(jù)庫(關(guān)系型或非關(guān)系型)庫表來存儲配置,然后再編寫工具進行數(shù)據(jù)的導(dǎo)入。這種存儲方案克服了配置文件的部分問題,對配置有更精細化的管理。但是也存在明顯的不足,即高度的定制化,不可復(fù)用,重復(fù)開發(fā)高。因此,我們需要對此進行完善,將配置的存儲、讀、寫、管理等過程提煉共性,通用化、平臺化。
方案思考
物理模型
既然配置文件難以精細化管理,且具備易侵入的物理實體(本地文件),我們需要新的數(shù)據(jù)結(jié)構(gòu)來承載配置。前文我們討論過,配置有兩種數(shù)據(jù)模型,分別是key=object以及key=table。對使用者而言,配置必須是可視、可讀、易管理的。為了達成這目的,我們只需在內(nèi)部運營人員與配置系統(tǒng)核心之間搭一套設(shè)計良好的運營系統(tǒng)即可。那么在后端呢?對消費者而言,最注重傳輸、計算的效率,同時為了與微服務(wù)框架的對齊,protobuf message無疑是最佳的形式。
然而 protobuf 無法自解釋,在沒有 message 定義的情況下,我們即沒辦法將文本性的配置轉(zhuǎn)換成 pb 二進制流,也沒辦法反序列化。因此必須將業(yè)務(wù)的 message 定義上提到運營系統(tǒng),然而 protobuf 卻對可視化編輯不太友好。因此一個可行的思路是基于JSON 數(shù)據(jù)進行配置的定義、可視化操作、傳輸及存儲。只有到達業(yè)務(wù)側(cè)才進行數(shù)據(jù)類型的轉(zhuǎn)換。
安全管理
搭建一套配置運營系統(tǒng),讓之成為運營人員管理配置的唯一入口,輕松就可以得到很高的回報。我們可以基于運營系統(tǒng)進行各種配置安全加固,如配置的變更必須具備相應(yīng)的權(quán)限,而且只有通過審核才能應(yīng)用到系統(tǒng),所有的操作都要有審計的能力,配置的歷史版本快速可查等。
同時灰度、回退等能力也需要基于運營系統(tǒng)進行操作。
配置系統(tǒng) SDK
上文提及,集中式配置文件系統(tǒng)的管道定位,agent 只負責定期的拉取配置然后緩存到本地的文件系統(tǒng)。業(yè)務(wù)系統(tǒng)與配置系統(tǒng)松耦合。我們認為配置文件仍然具有較高的開發(fā)成本,對業(yè)務(wù)方而言,最佳的開發(fā)形式應(yīng)當是:
- int GetConfig
(const std::string& key, ::google::protobuf::Message& msg);
而不需要再去理解文件內(nèi)容、形式。那么我們就有必要為業(yè)務(wù)方提供一套配置系統(tǒng)的 SDK,將配置系統(tǒng)的細節(jié)、數(shù)據(jù)結(jié)構(gòu)等信息都屏蔽起來,讓業(yè)務(wù)只看到配置的 Protobuf Message 對象。
在 SDK 的基礎(chǔ)上,消費者只需輕度介入(業(yè)務(wù)插件,見下),我們就可以完成協(xié)議轉(zhuǎn)換、配置緩存、進程,線程,協(xié)程快速最終一致、請求單調(diào)、灰度發(fā)布的能力。
配置系統(tǒng) SDK 是精細化管理的基礎(chǔ),我們可以通過維護配置本身內(nèi)容之外的配置元數(shù)據(jù)信息來完成上述能力。
異步化
異步化是配置 SDK 的關(guān)鍵。很多本地緩存的更新是周期性的由實時鏈路請求負責,易于實現(xiàn),但效率上存在問題,尤其考慮到我們還需要對配置進行配置業(yè)務(wù)邏輯的處理。因此,最佳方案應(yīng)當是通過異步過程來進行配置的加載、初始化及其它邏輯處理。
異步帶來的問題是異步過程與實時請求的并發(fā)問題,即異步過程在進行配置變更過程中,應(yīng)如何處理實時鏈路的讀請求,這是一個工程問題,我們會另文討論,一個可行的思路是多版本及引用計數(shù)技術(shù)。
業(yè)務(wù)插件
異步為我們提供的另外一個好處是,業(yè)務(wù)可以在配置生效的時候進行一些初始化動作,比如進行進行配置正確性校驗,以及搭建業(yè)務(wù)適合的數(shù)據(jù)結(jié)構(gòu)。比如業(yè)務(wù)白名單在 pb 中只是一個數(shù)組,如果業(yè)務(wù)進行命中查找,代價比較高。業(yè)務(wù)最期望的方式肯定還是使用 map 來存儲。因此配置 SDK 異步化,就為業(yè)務(wù)插件能力提供了基礎(chǔ)。
推與拉
我們更傾向于配置 SDK 主動拉取配置的更新。推與拉的辯證在于效率和可用性。推比較高效,不存在無用的網(wǎng)絡(luò)消耗。但是推又引入了新的系統(tǒng)依賴(即事件中心)。如無必要,勿增實體,基于這樣的思想,我們傾向于由SDK 周期性主動拉取。至于效率,完全可以通過各種工程的手段加以優(yōu)化,達到可以接受的程度。
當然這也取決于系統(tǒng)規(guī)模,如果我們要討論的是公司機的配置系統(tǒng),而不是部分中心級,那么我們也會認真的思考推或者推拉結(jié)合的模式。
快速最終一致
無論是單機配置文件系統(tǒng),還是集中配置文件系統(tǒng),都存在嚴重的不一致問題。對一次配置變更,基本上都需要很長的時間才能達到最終一致(即所有并發(fā)看到相同的數(shù)據(jù)狀態(tài))。
一個可行的思路是多版本以及定時生效。配置只有在未來的某個時間(該時間內(nèi) SDK 已經(jīng)拉到了最新數(shù)據(jù))才對外可見。至于如何確保所有 SDK 都拉到了數(shù)據(jù),這涉及到可用性的問題,我們另文討論。
請求單調(diào)
定時生效沒辦法解決請求單調(diào)性的問題。請求單調(diào)性是指,實時服務(wù)處理一次請求,在請求的調(diào)用棧過程中,讀到的配置內(nèi)容必須是靜態(tài)、沒有變動的,即使中間有待生效數(shù)據(jù)變成了生效數(shù)據(jù)。一個思路是我們可以通過線程私有變量(協(xié)程私有變量)緩存配置版本即可。
灰度發(fā)布
在配置 SDK 多版本能力的基礎(chǔ)上,實現(xiàn)灰度發(fā)布的能力也是輕而易舉的?;叶劝l(fā)布的能力,不過就是選擇生效配置版本的能力,如果本機、本角色、本請求業(yè)務(wù) key(如用戶、商戶、訂單)等命中灰度范圍,則使用新版本,否則使用原版本。
效率提升
效率提升包括降低網(wǎng)絡(luò)傳輸數(shù)據(jù)量、降低配置存儲服務(wù)的壓力,這些都是具體的工程手段,我們不在本理論篇內(nèi)討論。
可用性提升
分布式系統(tǒng)的可用性提升是老生常談的話題,為了聚焦于配置系統(tǒng)獨特的能力,我們本篇不專門進行討論。
(However,盡量減少系統(tǒng)中的單點,是一個重要的原則。在前節(jié)”推與拉“中也有涉及。同時為了業(yè)務(wù)的可用性,第三方配置系統(tǒng)的運營能力、故障主動發(fā)現(xiàn)能力、故障通知能力、再現(xiàn)及定位能力也非常重要。也這是重復(fù)造輪子的一個不得已的重要原因,很多團隊軟件可能作的不錯,但服務(wù)能力(主要指運營能力)卻有點差強人意。)
【本文為51CTO專欄作者“騰訊技術(shù)工程”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者(微信號:Tencent_TEG)】
當前標題:微信研發(fā)體系下的分布式配置系統(tǒng)設(shè)計概要
鏈接分享:http://www.5511xx.com/article/cdgpoes.html


咨詢
建站咨詢
