這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)怎么解析Nacos配置中心,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
蘿北網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)公司!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。成都創(chuàng)新互聯(lián)公司公司2013年成立到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)公司。
上回我們說到Nacos的注冊中心,我們講了注冊中心的一致性協(xié)議,訂閱和注冊的原理,有興趣的可以看一下上一篇文章:你應(yīng)該了解的Nacos注冊中心。在Nacos中還有一個(gè)功能特別重要那就是配置中心,在這里先不具體介紹配置中心是什么,先來憶苦思甜一波。
在我們最開始做一些簡單的學(xué)習(xí)項(xiàng)目的時(shí)候,我們會(huì)遇到一些需要配置的東西,比如數(shù)據(jù)庫連接池大小,用戶的黑名單等等,我們都把這些東西寫死在代碼里面,比如if(userId == 123){do something}
,這種代碼在項(xiàng)目里隨處可見。后來參加工作了,發(fā)現(xiàn)這種寫法并沒有將配置很好的統(tǒng)一管理起來,配置地方隨處可見,并且無法根據(jù)代碼環(huán)境去進(jìn)行調(diào)整,比如線上和線下都只能使用同一個(gè)配置,雖然可以通過if,else的方式,但是這個(gè)非常麻煩,所以在工作就開始使用xml,yaml等方式在文件里面進(jìn)行配置,在不同的運(yùn)行環(huán)境讀取不同的配置。這種方式基本滿足了大部分的需求,但是后面遇到了一個(gè)需要?jiǎng)討B(tài)去修改這些配置的情況,如果通過文件的方式我們就只能修改文件然后重新上線服務(wù),這樣是非常麻煩的,所以就誕生了配置中心。
我們在這里可以想想,如果你要實(shí)現(xiàn)配置中心,應(yīng)該具備哪些功能呢?我這里列舉一些:
可以動(dòng)態(tài)的修改配置。
配置中心掛了也不影響配置的使用。
配置是可以多個(gè)服務(wù)共享的。
支持權(quán)限管理,只有授予權(quán)限的人才能查看和修改配置
配置可以回滾,當(dāng)我們遇到配置出現(xiàn)問題的時(shí)候可以像回滾服務(wù)一樣回滾配置。
灰度發(fā)布,可以讓某幾臺(tái)機(jī)器先使用這個(gè)配置如果沒有問題,在進(jìn)行全量。
配置中心自身的QPS能保證足夠,如果是一個(gè)公司的基礎(chǔ)服務(wù)的話是需要保證這個(gè)的
其實(shí)在開源的項(xiàng)目中有挺多配置中心的開源的比如spring cloud config
, Apollo
等等,其中Apollo是攜程開源的配置中心,在業(yè)界也是非常出名,我們這邊文章主要還是介紹Nacos的配置中心,當(dāng)然有興趣的同學(xué)可以下來自行查看其他注冊中心相關(guān)介紹。
同樣的我們首先也先介紹一下和注冊中心相關(guān)的一些基本名詞概念:
命名空間(namespace):和注冊中心一樣,命名空間屬于Nacos頂層的結(jié)構(gòu),用于進(jìn)行租戶級別的隔離,我們最常用的就是不同環(huán)境比如測試環(huán)境,線上環(huán)境進(jìn)行隔離。
配置管理:系統(tǒng)配置的編輯、存儲(chǔ)、分發(fā)、變更管理、歷史版本管理、變更審計(jì)等所有與配置相關(guān)的活動(dòng)。
配置項(xiàng):一個(gè)具體的可配置的參數(shù)與其值域,通常以 param-key=param-value 的形式存在。例如我們常配置系統(tǒng)的日志輸出級別(logLevel=INFO|WARN|ERROR) 就是一個(gè)配置項(xiàng)。
配置集: 一組相關(guān)或者不相關(guān)的配置項(xiàng)的集合稱為配置集。在系統(tǒng)中,一個(gè)配置文件通常就是一個(gè)配置集,包含了系統(tǒng)各個(gè)方面的配置。例如,一個(gè)配置集可能包含了數(shù)據(jù)源、線程池、日志級別等配置項(xiàng)。
配置集 ID : Nacos 中的某個(gè)配置集的 ID。配置集 ID 是組織劃分配置的維度之一。
配置分組:Nacos 中的一組配置集,是組織配置的維度之一。
配置快照:Nacos 的客戶端 SDK 會(huì)在本地生成配置的快照。當(dāng)客戶端無法連接到 Nacos Server 時(shí),可以使用配置快照顯示系統(tǒng)的整體容災(zāi)能力。配置快照類似于 Git 中的本地 commit,也類似于緩存,會(huì)在適當(dāng)?shù)臅r(shí)機(jī)更新,但是并沒有緩存過期(expiration)的概念。
配置中心的架構(gòu)圖如下:
用戶可以在后臺(tái)界面進(jìn)行添加或者修改配置,也可以通過client-api進(jìn)行修改配置
所有修改的數(shù)據(jù)通過raft首先在Leader修改生效,然后同步至其他副本。
如果用戶想訂閱該配置通過long polling的方式進(jìn)行訂閱。
配置中心最為關(guān)鍵的就是如何去做好存儲(chǔ),一般我們存儲(chǔ)就兩種方式, 要么全內(nèi)存存儲(chǔ),能保證性能非常高,但是維護(hù)不同機(jī)器內(nèi)存一致性復(fù)雜度比較高,還有一種就是使用數(shù)據(jù)庫,內(nèi)存里面不維護(hù)任何狀態(tài),每一臺(tái)機(jī)器都可以進(jìn)行寫入操作,這個(gè)復(fù)雜度比較低,不需要考慮一致性的問題,但是由于所有的讀寫都會(huì)走數(shù)據(jù)庫所以性能就不能保證。在Nacos中對這兩種存儲(chǔ)方式做了一些改進(jìn),實(shí)現(xiàn)了既保證了性能又保證了復(fù)雜度一致性。
在Nacos1.3之后提供了MySQL 和 raft + derby兩種存儲(chǔ)方式,接下來介紹一一介紹一下這兩種存儲(chǔ)方式。
nacos最開始提供的就是mysql的方式,所有機(jī)器都可以進(jìn)行讀寫,沒有主備之分,如下圖所示:
如果只是使用mysql,有同學(xué)會(huì)提出問題,只使用mysql如何才能保證數(shù)據(jù)庫性能不會(huì)成為瓶頸呢?最簡單的方法就是使用高配置的Mysql,用錢給我干上去,很明顯這個(gè)不是很靠譜,只適用于土豪玩家。那么怎么去做這種優(yōu)化呢?一般做業(yè)務(wù)的同學(xué)通常會(huì)在Mysql前面放一層緩存層,比如redis,memcached等等。
在Nacos中同樣的也使用了緩存這個(gè)概念幫助我們緩解數(shù)據(jù)庫壓力。但是和普通的緩存稍微有點(diǎn)不同:
在ConfigService中有一個(gè)HashMap緩存了所有Config的元數(shù)據(jù)(MD5,類型這些數(shù)據(jù))
但是對于具體存儲(chǔ)的值我們不會(huì)直接放在內(nèi)存,而是存儲(chǔ)到了本地磁盤,這么做的好處是因?yàn)槲覀兊腸onfig所配置的值我們不能保證他的大小,如果每個(gè)config的值都很大,那么我們的內(nèi)存必然會(huì)不足,這個(gè)時(shí)候Nacos和Apollo 兩個(gè)開源中間件給出兩種解法:
Apollo的做法是使用一個(gè)guavaCache,使用淘汰策略將不經(jīng)常使用的進(jìn)行淘汰。
Nacos的做法是全量緩存元數(shù)據(jù),具體的值存儲(chǔ)到磁盤空間,采用分離存儲(chǔ)的方式,nacos采用這種方法,如果只是訪問元數(shù)據(jù)那么全量內(nèi)存即可,不會(huì)像Apollo一樣可能會(huì)遇到淘汰的原因,訪問數(shù)據(jù)庫。
Nacos使用的是全量緩存元數(shù)據(jù)到內(nèi)存,具體的值存儲(chǔ)到磁盤空間,但是會(huì)存在一個(gè)問題,那就是當(dāng)一臺(tái)機(jī)器的數(shù)據(jù)發(fā)生變更,其他機(jī)器的內(nèi)存怎么變更呢?這就需要我們的全量異步通知,在每一次修改數(shù)據(jù)的時(shí)候都會(huì)發(fā)送一個(gè)ConfigDataChage事件,然后本機(jī)接受并進(jìn)行處理,然后發(fā)送這個(gè)變更消息到其他的所有機(jī)器上。
其他機(jī)器收到這個(gè)變更通知之后,會(huì)進(jìn)行一次dump操作:
會(huì)先查詢元數(shù)據(jù)中的MD5,MD5其實(shí)也是根據(jù)我們配置中的值算出來的,所以能進(jìn)行快速判斷這一次時(shí)候發(fā)生了值的變更,如果發(fā)生變更,我們就將這個(gè)值存儲(chǔ)到磁盤上。
如果我們這個(gè)機(jī)器是新啟動(dòng)的,這個(gè)時(shí)候其實(shí)就不會(huì)存在任何緩存以及dump文件,那么DumpService會(huì)遍歷數(shù)據(jù)庫的所有數(shù)據(jù),全量的都緩存到機(jī)器上,以便我們使用。
Nacos在1.3.0之后提供了一個(gè)新的存儲(chǔ)模式,那就是使用raft協(xié)議保證數(shù)據(jù)一致性,使用apache derby進(jìn)行內(nèi)嵌的數(shù)據(jù)存儲(chǔ)。提供這種方式的目的是減少用戶維護(hù)mysql數(shù)據(jù)庫集群的成本,并且簡化了集群部署的成本,部署Nacos的時(shí)候直接打包Nacos鏡像就好,不需要再單獨(dú)部署一套數(shù)據(jù)庫。
在Nacos中使用的是sofa-jraft,這個(gè)是螞蟻開源的一個(gè)java版本高性能的raft實(shí)現(xiàn),不熟悉raft的同學(xué)可以閱讀以下raft的論文,了解過raft的同學(xué)應(yīng)該都知道raft非常強(qiáng)化Leader的概念:
系統(tǒng)中必須存在且同一時(shí)刻只能有一個(gè) leader,只有 leader 可以接受 clients 發(fā)過來的請求
Leader 負(fù)責(zé)主動(dòng)與所有 followers 通信,負(fù)責(zé)將’提案’發(fā)送給所有 followers,同時(shí)收集多數(shù)派的 followers 應(yīng)答
Leader 還需向所有 followers 主動(dòng)發(fā)送心跳維持領(lǐng)導(dǎo)地位
我們發(fā)現(xiàn)所有的事情都和leader相關(guān),那么我們的性能必定被限制在leader上面,所以在Nacos中選擇了對raft本身有大量優(yōu)化的sofa-jraft,在sofa-jraft中做了如下的優(yōu)化:
批量化:批量化操作是很多系統(tǒng)的一個(gè)優(yōu)化策略,在jraft中同樣的也采用了批量化操作,通過disruptor 的 MPSC 模型批量消費(fèi),實(shí)現(xiàn)了下面的一些批量操作,提升了很多的性能:
批量提交 task
批量網(wǎng)絡(luò)發(fā)送
本地 IO batch 寫入
批量應(yīng)用到狀態(tài)機(jī)
pipeline復(fù)制: pipeline是一種管道技術(shù),幫助我們不再和以前請求-響應(yīng)模型一樣,他可以持續(xù)往管道中放入請求,過程中而不需要等待請求的回復(fù),在最后再一并讀取結(jié)果即可。在jraft中開啟pipeline性能會(huì)提升30%。
并行化:leader持久化log和發(fā)送Log到follower是并行的,發(fā)送到不同的follower也是并行的。
線性讀:在raft協(xié)議中,讀請求會(huì)按照 Log 處理,通過 Log 復(fù)制和狀態(tài)機(jī)執(zhí)行來得到讀結(jié)果,然后再把結(jié)果返回給 Client。這種辦法的缺點(diǎn)是需要 Log 存儲(chǔ)、復(fù)制,這樣會(huì)帶來刷盤開銷、存儲(chǔ)開銷、網(wǎng)絡(luò)開銷,因此在讀操作很多的場景下對性能影響很大。在Sofajrat中進(jìn)行了ReadIndex,Lease Read優(yōu)化,讓所有的讀都可以在本地執(zhí)行,這個(gè)對性能的提升特別大。
Apache Derby也是一個(gè)Java編寫的輕量級數(shù)據(jù)庫,Nacos通過這樣的設(shè)計(jì)其實(shí)是構(gòu)建了一個(gè)輕量級的分布式數(shù)據(jù)庫,在每一臺(tái)的機(jī)器上都會(huì)有一個(gè)保存數(shù)據(jù)的數(shù)據(jù)庫,然后通過raft協(xié)議保證所有機(jī)器數(shù)據(jù)的一致性。
內(nèi)嵌數(shù)據(jù)庫的方式并不比Mysql的方式更好,在性能上Mysql那種方式因?yàn)榇媪撕芏嗑彺?,并且content也保存到磁盤上,讀取的時(shí)候基本不會(huì)走庫,所以Mysql的方式其實(shí)更好,但是內(nèi)嵌數(shù)據(jù)庫的方式在運(yùn)維部署的方式上是非常占優(yōu)的。這里如何取舍需要用戶自己進(jìn)行一個(gè)選擇
我們在上一節(jié)說到Nacos注冊中心中的訂閱是通過udp廣播+定時(shí)輪訓(xùn)來獲取到,而在配置中心中采用的是長輪訓(xùn)的方式進(jìn)行訂閱變更,為什么這兩個(gè)實(shí)現(xiàn)訂閱會(huì)采用不同的方式來實(shí)現(xiàn)呢?我們注冊中心中所保存的數(shù)據(jù)都是小數(shù)據(jù)比如節(jié)點(diǎn)的Ip,端口等信息,但是我們在配置中心中你不能控制配置的大小,比如一個(gè)服務(wù)訂閱了100個(gè)配置,每個(gè)配置的數(shù)據(jù)大小是1M,如果按照定時(shí)輪訓(xùn)的做法每次會(huì)拉100M的數(shù)據(jù),顯然是不靠譜的,所以這里采用了長輪訓(xùn)的方式,具體長輪訓(xùn)的方式如下:
Step1: 客戶端定時(shí)發(fā)出長輪訓(xùn)的請求,超時(shí)時(shí)間默認(rèn)為30s。發(fā)出的請求是自己所有訂閱配置內(nèi)容的MD5,這里我們不會(huì)把整個(gè)內(nèi)容當(dāng)成請求發(fā)出,不然又會(huì)出現(xiàn)上面所說的每次都會(huì)發(fā)出很多的數(shù)據(jù)。
Step2: 服務(wù)端收到這個(gè)請求后利用Servlet3.0的特性,開啟了異步AsyncContext。
Step3: 服務(wù)端存儲(chǔ)這個(gè)AsyncContext,等待配置的變更,數(shù)據(jù)的變更會(huì)通過DataChangeEvent事件中進(jìn)行觸發(fā),然后判斷之前請求中的md5和新更新的md5是否一樣,如果一致將變更信息寫入到AsyncContext的response中。
Ste4: 如果超時(shí)還沒有到,那么代表本次沒有配置進(jìn)行更新,又會(huì)回到Step1。
通過這樣的方式,我們每次請求量都會(huì)很少,只有在數(shù)據(jù)真正更新的時(shí)候才會(huì)將真正的數(shù)據(jù)返回給我們。
我們可能有這樣的一個(gè)需求,我們需要驗(yàn)證某個(gè)配置是否對業(yè)務(wù)上有影響,通常的配置中心都是直接修改,所有機(jī)器全量都會(huì)被更新,如果這個(gè)配置出現(xiàn)問題,那么就會(huì)全量的出現(xiàn)故障。在Nacos中提供了一個(gè)灰度的功能,我們可以將某個(gè)配置只給某一些機(jī)器使用,這樣就可以完成一些小流量驗(yàn)證。
在Nacos中灰度發(fā)布也叫做beta發(fā)布,如下圖所示:
在nacos中的具體實(shí)現(xiàn)的是用一個(gè)單獨(dú)的表去保存beta相關(guān)的信息:
用beta_ips字段保存了我們需要灰度的機(jī)器,在客戶端訂閱進(jìn)行長輪訓(xùn)的時(shí)候,也會(huì)過濾是否是灰度的機(jī)器,如果是才會(huì)進(jìn)行更新,下面是LongPollingService的代碼:
在Nacos中也提供了歷史版本,類似git的commit一樣,只要你有commitid你就能回滾到對應(yīng)的版本,在Nacos用了一個(gè)history_config表來進(jìn)行保存,我們可以通過這個(gè)表獲取我們某個(gè)配置的所有歷史,以此來進(jìn)行回滾。
在Nacos中還有很多其他的功能,比如權(quán)限管理等等,在這里我就不一一介紹了。在Nacos的配置中心中設(shè)計(jì)得最為巧妙的也就是存儲(chǔ)和訂閱了,存儲(chǔ)Nacos提供了兩種模式,一個(gè)是Mysql+緩存+本次磁盤的方式,還有一種是通過raft+derby的方式,都有自己的優(yōu)劣點(diǎn)。訂閱的話Nacos采用的和注冊中心完全不一樣的方式,通過長輪訓(xùn)很好的解決了更新的實(shí)時(shí)通知,并且不需要大量請求資源。如果大家對Nacos感興趣,建議還是可以閱讀下Nacos的代碼。
上述就是小編為大家分享的怎么解析Nacos配置中心了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。