亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Nacos注冊(cè)中心和配置中心的底層原理全面解讀

 更新時(shí)間:2025年06月06日 11:05:03   作者:奇怪的爪哇島開發(fā)  
這篇文章主要介紹了Nacos注冊(cè)中心和配置中心的底層原理的全面解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

臨時(shí)實(shí)例和永久實(shí)例

臨時(shí)實(shí)例

在注冊(cè)到注冊(cè)中心之后僅僅只保存在服務(wù)端內(nèi)部一個(gè)緩存中,不會(huì)持久化到磁盤

這個(gè)服務(wù)端內(nèi)部的緩存在注冊(cè)中心屆一般被稱為服務(wù)注冊(cè)表

當(dāng)服務(wù)實(shí)例出現(xiàn)異?;蛘呦戮€之后,就會(huì)把這個(gè)服務(wù)實(shí)例從服務(wù)注冊(cè)表中剔除

永久服務(wù)

實(shí)例不僅僅會(huì)存在服務(wù)注冊(cè)表中,同時(shí)也會(huì)被持久化到磁盤文件

當(dāng)服務(wù)實(shí)例出現(xiàn)異?;蛘呦戮€,Nacos 只會(huì)將服務(wù)實(shí)例的健康狀態(tài)設(shè)置為不健康,并不會(huì)對(duì)將其從服務(wù)注冊(cè)表中剔除

所以這個(gè)服務(wù)實(shí)例的信息你還是可以從注冊(cè)中心看到,只不過處于不健康狀態(tài)

為什么 Nacos 要將服務(wù)實(shí)例分為臨時(shí)實(shí)例和永久實(shí)例?

臨時(shí)實(shí)例就比較適合于業(yè)務(wù)服務(wù),服務(wù)下線之后可以不需要在注冊(cè)中心中查看到

永久實(shí)例就比較適合需要運(yùn)維的服務(wù),這種服務(wù)幾乎是永久存在的,比如說 MySQL、Redis 等等

當(dāng)然如果你想改成永久實(shí)例,可以通過下面這個(gè)配置項(xiàng)來完成

spring  
    cloud:    
        nacos:      
            discovery:        #ephemeral單詞是臨時(shí)的意思,設(shè)置成false,就是永久實(shí)例了                          ephemeral: false

1.x 版本和2.x版本的區(qū)別

在 1.x 版本中,一個(gè)服務(wù)中可以既有臨時(shí)實(shí)例也有永久實(shí)例,服務(wù)實(shí)例是永久還是臨時(shí)是由服務(wù)實(shí)例本身決定的

但是 2.x 版本中,一個(gè)服務(wù)中的所有實(shí)例要么都是臨時(shí)的要么都是永久的,是由服務(wù)決定的,而不是具體的服務(wù)實(shí)例

服務(wù)注冊(cè)

作為一個(gè)服務(wù)注冊(cè)中心,服務(wù)注冊(cè)肯定是一個(gè)非常重要的功能

所謂的服務(wù)注冊(cè),就是通過注冊(cè)中心提供的客戶端 SDK(或者是控制臺(tái))將服務(wù)本身的一些元信息,比如 ip、端口等信息發(fā)送到注冊(cè)中心服務(wù)端

服務(wù)端在接收到服務(wù)之后,會(huì)將服務(wù)的信息保存到前面提到的服務(wù)注冊(cè)表中

1.x 版本的實(shí)現(xiàn)

服務(wù)注冊(cè)是通過 Http 接口實(shí)現(xiàn)的,Nacos 服務(wù)端本身就是用 SpringBoot 寫的

代碼如下

2.x 版本的實(shí)現(xiàn)

2.x 版本相比于 1.x 版本最主要的升級(jí)就是客戶端和服務(wù)端通信協(xié)議的改變,由 1.x 版本的 Http 改成了 2.x 版本 gRPC

之所以改成了 gRPC,主要是因?yàn)?Http 請(qǐng)求會(huì)頻繁創(chuàng)建和銷毀連接,白白浪費(fèi)資源

所以在 2.x 版本之后,為了提升性能,就將通信協(xié)議改成了 gRPC

根據(jù)官網(wǎng)顯示,整體的效果還是很明顯,相比于 1.x 版本,注冊(cè)性能總體提升至少 2 倍

具體實(shí)現(xiàn)

Nacos 客戶端在啟動(dòng)的時(shí)候,會(huì)通過 gRPC 跟服務(wù)端建立長連接

當(dāng)客戶端發(fā)起注冊(cè)的時(shí)候,就會(huì)通過這個(gè)長連接,將服務(wù)實(shí)例的信息發(fā)送給服務(wù)端

服務(wù)端拿到服務(wù)實(shí)例,跟 1.x 一樣,也會(huì)存到服務(wù)注冊(cè)表

除了注冊(cè)之外,當(dāng)注冊(cè)的是臨時(shí)實(shí)例時(shí),2.x 還會(huì)將服務(wù)實(shí)例信息存儲(chǔ)到客戶端中的一個(gè)緩存中,供 Redo 操作

所謂的 Redo 操作,其實(shí)就是一個(gè)補(bǔ)償機(jī)制,本質(zhì)是個(gè)定時(shí)任務(wù),默認(rèn)每 3s 執(zhí)行一次

這個(gè)定時(shí)任務(wù)作用是,當(dāng)客戶端與服務(wù)端重新建立連接時(shí)(因?yàn)橐恍┊惓T驅(qū)е逻B接斷開)

那么之前注冊(cè)的服務(wù)實(shí)例肯定還要繼續(xù)注冊(cè)服務(wù)端(斷開連接服務(wù)實(shí)例就會(huì)被剔除服務(wù)注冊(cè)表

所以這個(gè) Redo 操作一個(gè)很重要的作用就是重連之后的重新注冊(cè)的作用

除了注冊(cè)之外,比如服務(wù)訂閱之類的操作也需要 Redo 操作,當(dāng)連接重新建立,之前客戶端的操作都需要 Redo 一下

心跳機(jī)制(直接解決了臨時(shí)實(shí)例的心跳機(jī)制)

心跳機(jī)制,也可以被稱為?;顧C(jī)制,它的作用就是服務(wù)實(shí)例告訴注冊(cè)中心我這個(gè)服務(wù)實(shí)例還活著

正常情況下,服務(wù)關(guān)閉了,那么服務(wù)會(huì)主動(dòng)向 Nacos 服務(wù)端發(fā)送一個(gè)服務(wù)下線的請(qǐng)求

Nacos 服務(wù)端在接收到請(qǐng)求之后,會(huì)將這個(gè)服務(wù)實(shí)例從服務(wù)注冊(cè)表中剔除

但是對(duì)于異常情況下,比如出現(xiàn)網(wǎng)絡(luò)問題,可能導(dǎo)致這個(gè)注冊(cè)的服務(wù)實(shí)例無法提供服務(wù),處于不可用狀態(tài),也就是不健康

而此時(shí)在沒有任何機(jī)制的情況下,服務(wù)端是無法知道這個(gè)服務(wù)處于不可用狀態(tài)

所以為了避免這種情況,一些注冊(cè)中心,就比如 Nacos、Eureka,就會(huì)用心跳機(jī)制來判斷這個(gè)服務(wù)實(shí)例是否能正常

在 Nacos 中,心跳機(jī)制僅僅是針對(duì)臨時(shí)實(shí)例來說的,臨時(shí)實(shí)例需要靠心跳機(jī)制來?;?/strong>

1.x 心跳實(shí)現(xiàn)

在 1.x 中,心跳機(jī)制實(shí)現(xiàn)是通過客戶端和服務(wù)端各存在的一個(gè)定時(shí)任務(wù)來完成的

在服務(wù)注冊(cè)時(shí),發(fā)現(xiàn)是臨時(shí)實(shí)例,客戶端會(huì)開啟一個(gè) 5s 執(zhí)行一次的定時(shí)任務(wù)

這個(gè)定時(shí)任務(wù)會(huì)構(gòu)建一個(gè) Http 請(qǐng)求,攜帶這個(gè)服務(wù)實(shí)例的信息,然后發(fā)送到服務(wù)端

在 Nacos 服務(wù)端也會(huì)開啟一個(gè)定時(shí)任務(wù),默認(rèn)也是 5s 執(zhí)行一次,去檢查這些服務(wù)實(shí)例最后一次心跳的時(shí)間,也就是客戶端最后一次發(fā)送 Http 請(qǐng)求的時(shí)間

  • 當(dāng)最后一次心跳時(shí)間超過 15s,但沒有超過 30s,會(huì)把這服務(wù)實(shí)例標(biāo)記成不健康
  • 當(dāng)最后一次心跳超過 30s,直接把服務(wù)從服務(wù)注冊(cè)表中剔除

1.x 版本的心跳機(jī)制,本質(zhì)就是兩個(gè)定時(shí)任務(wù)

其實(shí) 1.x 的這個(gè)心跳還有一個(gè)作用,就是跟上一節(jié)說的 gRPC 時(shí) Redo 操作的作用是一樣的

服務(wù)在處理心跳的時(shí)候,發(fā)現(xiàn)心跳攜帶這個(gè)服務(wù)實(shí)例的信息在注冊(cè)表中沒有,此時(shí)就會(huì)添加到服務(wù)注冊(cè)表

所以心跳也有 Redo 的類似效果

2.x 心跳實(shí)現(xiàn)(兼容1.x版本心跳機(jī)制,如果客戶端使用的SDK是1.x的情況下)

在 2.x 版本之后,由于通信協(xié)議改成了 gRPC,客戶端與服務(wù)端保持長連接,所以 2.x 版本之后它是利用這個(gè) gRPC 長連接本身的心跳來保活

一旦這個(gè)連接斷開,服務(wù)端就會(huì)認(rèn)為這個(gè)連接注冊(cè)的服務(wù)實(shí)例不可用,之后就會(huì)將這個(gè)服務(wù)實(shí)例從服務(wù)注冊(cè)表中提出剔除

除了連接本身的心跳之外,Nacos 還有服務(wù)端的一個(gè)主動(dòng)檢測(cè)機(jī)制

Nacos 服務(wù)端也會(huì)啟動(dòng)一個(gè)定時(shí)任務(wù),默認(rèn)每隔 3s 執(zhí)行一次

這個(gè)任務(wù)會(huì)去檢查超過 20s 沒有發(fā)送請(qǐng)求數(shù)據(jù)的連接

一旦發(fā)現(xiàn)有連接已經(jīng)超過 20s 沒發(fā)送請(qǐng)求,那么就會(huì)向這個(gè)連接對(duì)應(yīng)的客戶端發(fā)送一個(gè)請(qǐng)求

如果請(qǐng)求不通或者響應(yīng)失敗,此時(shí)服務(wù)端也會(huì)認(rèn)為與客戶端的這個(gè)連接異常,從而將這個(gè)客戶端注冊(cè)的服務(wù)實(shí)例從服務(wù)注冊(cè)表中剔除

所以對(duì)于 2.x 版本,主要是兩種機(jī)制來進(jìn)行?;睿?/p>

基于gRPC長連接雙向活躍檢測(cè):gRPC 連接未立即斷開,但數(shù)據(jù)無法完整往返

  • 客戶端進(jìn)程卡死但心跳線程存活(假心跳場景) 例如:應(yīng)用主線程死鎖,但心跳線程仍能發(fā)送TCP包,此時(shí)服務(wù)實(shí)際不可用,但心跳正常。
  • 網(wǎng)絡(luò)隔離(Network Partition)(假心跳場景) 客戶端與Nacos Server之間出現(xiàn)單向網(wǎng)絡(luò)故障(客戶端能發(fā)數(shù)據(jù),但收不到響應(yīng))。

Nacos 主動(dòng)檢查機(jī)制(連接存活但不活躍),服務(wù)端會(huì)對(duì) 20s 沒有發(fā)送數(shù)據(jù)的連接進(jìn)行檢查,出現(xiàn)異常時(shí)也會(huì)主動(dòng)斷開連接,剔除服務(wù)實(shí)例

健康檢查(為解決永久實(shí)例的心跳機(jī)制)

心跳機(jī)制僅僅是臨時(shí)實(shí)例用來保護(hù)的機(jī)制

而對(duì)于永久實(shí)例來說,一般來說無法主動(dòng)上報(bào)心跳

就比如說 MySQL 實(shí)例,肯定是不會(huì)主動(dòng)上報(bào)心跳到 Nacos 的,所以這就導(dǎo)致無法通過心跳機(jī)制來?;?/p>

所以針對(duì)永久實(shí)例的情況,Nacos 通過一種叫健康檢查的機(jī)制去判斷服務(wù)實(shí)例是否活著

健康檢查跟心跳機(jī)制剛好相反,心跳機(jī)制是服務(wù)實(shí)例向服務(wù)端發(fā)送請(qǐng)求

而所謂的健康檢查就是服務(wù)端主動(dòng)向服務(wù)實(shí)例發(fā)送請(qǐng)求,去探測(cè)服務(wù)實(shí)例是否活著

健康檢查機(jī)制在 1.x 和 2.x 的實(shí)現(xiàn)機(jī)制是一樣的

Nacos 服務(wù)端在會(huì)去創(chuàng)建一個(gè)健康檢查任務(wù),這個(gè)任務(wù)每次執(zhí)行時(shí)間間隔會(huì)在 2000~7000 毫秒之間

當(dāng)任務(wù)觸發(fā)的時(shí)候,會(huì)根據(jù)設(shè)置的健康檢查的方式執(zhí)行不同的邏輯,目前主要有以下三種方式:

TCP

  • 根據(jù)服務(wù)實(shí)例的 ip 和端口去判斷是否能連接成功,如果連接成功,就認(rèn)為健康,反之就任務(wù)不健康

HTTP

  • 向服務(wù)實(shí)例的 ip 和端口發(fā)送一個(gè) Http 請(qǐng)求,請(qǐng)求路徑是需要設(shè)置的,如果能正常請(qǐng)求,說明實(shí)例健康,反之就不健康

MySQL

  • 一種特殊的檢查方式,他可以執(zhí)行下面這條 Sql 來判斷數(shù)據(jù)庫是不是主庫

默認(rèn)情況下,都是通過 TCP 的方式來探測(cè)服務(wù)實(shí)例是否還活著

服務(wù)發(fā)現(xiàn)

所謂的服務(wù)發(fā)現(xiàn)就是指當(dāng)有服務(wù)實(shí)例注冊(cè)成功之后,其它服務(wù)可以發(fā)現(xiàn)這些服務(wù)實(shí)例

Nacos 提供了兩種發(fā)現(xiàn)方式:

主動(dòng)查詢

  • 指客戶端主動(dòng)向服務(wù)端查詢需要關(guān)注的服務(wù)實(shí)例,也就是拉(pull)的模式

服務(wù)訂閱

  • 指客戶端向服務(wù)端發(fā)送一個(gè)訂閱服務(wù)的請(qǐng)求,當(dāng)被訂閱的服務(wù)有信息變動(dòng)就會(huì)主動(dòng)將服務(wù)實(shí)例的信息推送給訂閱的客戶端,本質(zhì)就是推(push)模式

在我們平時(shí)使用時(shí),一般來說都是選擇使用訂閱的方式,這樣一旦有服務(wù)實(shí)例數(shù)據(jù)的變動(dòng),客戶端能夠第一時(shí)間感知

并且 Nacos 在整合 SpringCloud 的時(shí)候,默認(rèn)就是使用訂閱的方式

對(duì)于這兩種服務(wù)發(fā)現(xiàn)方式,1.x 和 2.x 版本實(shí)現(xiàn)也是不一樣

服務(wù)(主動(dòng))查詢

1.x 整體就是發(fā)送 Http 請(qǐng)求去查詢服務(wù)實(shí)例,2.x 只不過是將 Http 請(qǐng)求換成了 gRPC 的請(qǐng)求

服務(wù)端對(duì)于查詢的處理過程都是一樣的,從服務(wù)注冊(cè)表中查出符合查詢條件的服務(wù)實(shí)例進(jìn)行返回

服務(wù)訂閱

不過對(duì)于服務(wù)訂閱,兩者的機(jī)制就稍微復(fù)雜一點(diǎn)

不論是 1.x 還是 2.x 都是通過 SDK 中的NamingService#subscribe方法來發(fā)起訂閱的

當(dāng)有服務(wù)實(shí)例數(shù)據(jù)變動(dòng)的時(shí),客戶端就會(huì)回調(diào)EventListener,就可以拿到最新的服務(wù)實(shí)例數(shù)據(jù)了

1.x服務(wù)發(fā)現(xiàn)訂閱實(shí)現(xiàn)

客戶端在啟動(dòng)的時(shí)候,會(huì)去構(gòu)建一個(gè)叫 PushReceiver 的類

  • 這個(gè)類會(huì)去創(chuàng)建一個(gè) UDP Socket,端口是隨機(jī)的
  • 作用:通過 UDP 的方式接收服務(wù)端推送的數(shù)據(jù)的

調(diào)用NamingService#subscribe來發(fā)起訂閱時(shí),會(huì)先去服務(wù)端查詢需要訂閱服務(wù)的所有實(shí)例信息之后會(huì)將所有服務(wù)實(shí)例數(shù)據(jù)存到客戶端的一個(gè)內(nèi)部緩存中

  • 并且在查詢的時(shí)候,會(huì)將這個(gè) UDP Socket 的端口作為一個(gè)參數(shù)傳到服務(wù)端
  • 服務(wù)端接收到這個(gè) UDP 端口后,后續(xù)就通過這個(gè)端口給客戶端推送服務(wù)實(shí)例數(shù)據(jù)

會(huì)為這次訂閱開啟一個(gè)不定時(shí)執(zhí)行的任務(wù)

之所以不定時(shí),是因?yàn)檫@個(gè)當(dāng)執(zhí)行異常的時(shí)候,下次執(zhí)行的時(shí)間間隔就會(huì)變長,但是最多不超過 60s,正常是 10s,這個(gè) 10s 是查詢服務(wù)實(shí)例是服務(wù)端返回的

這個(gè)任務(wù)會(huì)去從服務(wù)端查詢訂閱的服務(wù)實(shí)例信息,然后更新內(nèi)部緩存

既然有了服務(wù)變動(dòng)推送的功能,為什么還要定時(shí)去查詢更新服務(wù)實(shí)例信息呢?

  • 那就是因?yàn)?UDP 通信不穩(wěn)定導(dǎo)致的
  • 雖然有 Push,但是由于 UDP 通信自身的不確定性,有可能會(huì)導(dǎo)致客戶端接收變動(dòng)信息失敗
  • 所以這里就加了一個(gè)定時(shí)任務(wù),彌補(bǔ)這種可能性,屬于一個(gè)兜底的方案。

2.x服務(wù)發(fā)現(xiàn)訂閱實(shí)現(xiàn)

由于 2.x 版本換成了 gRPC 長連接的方式,所以 2.x 版本服務(wù)數(shù)據(jù)變更推送已經(jīng)完全拋棄了 1.x 的 UDP 做法

當(dāng)有服務(wù)實(shí)例變動(dòng)的時(shí)候,服務(wù)端直接通過這個(gè)長連接將服務(wù)信息發(fā)送給客戶端

客戶端拿到最新服務(wù)實(shí)例數(shù)據(jù)之后的處理方式就跟 1.x 是一樣了

除了處理方式一樣,2.x 也繼承了 1.x 的其他的東西

比如客戶端依然會(huì)有服務(wù)實(shí)例的緩存

定時(shí)對(duì)比機(jī)制也保留了,只不過這個(gè)定時(shí)對(duì)比的機(jī)制默認(rèn)是關(guān)閉狀態(tài)

之所以默認(rèn)關(guān)閉,主要還是因?yàn)?/strong>長連接還是比較穩(wěn)定的原因

當(dāng)客戶端出現(xiàn)異常,接收不到請(qǐng)求,那么服務(wù)端會(huì)直接跟客戶端斷開連接

當(dāng)恢復(fù)正常,由于有 Redo 操作,所以還是能拿到最新的實(shí)例信息的

細(xì)節(jié)

1.x 版本的時(shí)候,任何服務(wù)都是可以被訂閱的

但是在 2.x 版本中,只支持訂閱臨時(shí)服務(wù),對(duì)于永久服務(wù),已經(jīng)不支持訂閱了

數(shù)據(jù)一致性

由于 Nacos 是支持集群模式的,所以一定會(huì)涉及到分布式系統(tǒng)中不可避免的數(shù)據(jù)一致性問題

服務(wù)實(shí)例的責(zé)任機(jī)制

什么是服務(wù)實(shí)例的責(zé)任機(jī)制?

比如上面提到的服務(wù)注冊(cè)、心跳管理、監(jiān)控檢查機(jī)制,當(dāng)只有一個(gè) Nacos 服務(wù)時(shí),那么自然而言這個(gè)服務(wù)會(huì)去檢查所有的服務(wù)實(shí)例的心跳時(shí)間,執(zhí)行所有服務(wù)實(shí)例的健康檢查任務(wù)

但是當(dāng)出現(xiàn) Nacos 服務(wù)出現(xiàn)集群時(shí),為了平衡各 Nacos 服務(wù)的壓力,Nacos 會(huì)根據(jù)一定的規(guī)則讓每個(gè) Nacos 服務(wù)只管理一部分服務(wù)實(shí)例的

當(dāng)然每個(gè) Nacos 服務(wù)的注冊(cè)表還是全部的服務(wù)實(shí)例數(shù)據(jù)

這個(gè)管理機(jī)制我給他起了一個(gè)名字,就叫做責(zé)任機(jī)制,因?yàn)槲以?1.x 和 2.x 都提到了responsible這個(gè)單詞

本質(zhì)就是 Nacos 服務(wù)對(duì)哪些服務(wù)實(shí)例負(fù)有心跳監(jiān)測(cè),健康檢查的責(zé)任。

BASE 理論(CAP妥協(xié)之后的產(chǎn)物)

  • 基本可用(Basically Available):系統(tǒng)出現(xiàn)故障還是能夠?qū)ν馓峁┓?wù),不至于直接無法用了
  • 軟狀態(tài)(Soft State):允許各個(gè)節(jié)點(diǎn)的數(shù)據(jù)短暫的不一致
  • 最終一致性,(Eventually Consistent):雖然允許各個(gè)節(jié)點(diǎn)的數(shù)據(jù)不一致,但是在一定時(shí)間之后,各個(gè)節(jié)點(diǎn)的數(shù)據(jù)最終需要一致的

Nacos 的 AP 和 CP

Nacos 其實(shí)目前是同時(shí)支持 AP 和 CP 的

具體使用 AP 還是 CP 得取決于 Nacos 內(nèi)部的具體功能,并不是有的文章說的可以通過一個(gè)配置自由切換。

就以服務(wù)注冊(cè)舉例來說,對(duì)于臨時(shí)實(shí)例來說,Nacos 會(huì)優(yōu)先保證可用性,也就是 AP

對(duì)于永久實(shí)例,Nacos 會(huì)優(yōu)先保證數(shù)據(jù)的一致性,也就是 CP

Nacos 的 AP 實(shí)現(xiàn)

對(duì)于 AP 來說,Nacos 使用的是阿里自研的 Distro 協(xié)議

在這個(gè)協(xié)議中,每個(gè)服務(wù)端節(jié)點(diǎn)是一個(gè)平等的狀態(tài),每個(gè)服務(wù)端節(jié)點(diǎn)正常情況下數(shù)據(jù)是一樣的,每個(gè)服務(wù)端節(jié)點(diǎn)都可以接收來自客戶端的讀寫請(qǐng)求

當(dāng)某個(gè)節(jié)點(diǎn)剛啟動(dòng)時(shí),他會(huì)向集群中的某個(gè)節(jié)點(diǎn)發(fā)送請(qǐng)求,拉取所有的服務(wù)實(shí)例數(shù)據(jù)到自己的服務(wù)注冊(cè)表中

這樣其它客戶端就可以從這個(gè)服務(wù)節(jié)點(diǎn)中獲取到服務(wù)實(shí)例數(shù)據(jù)了

當(dāng)某個(gè)服務(wù)端節(jié)點(diǎn)接收到注冊(cè)臨時(shí)服務(wù)實(shí)例的請(qǐng)求,不僅僅會(huì)將這個(gè)服務(wù)實(shí)例存到自身的服務(wù)注冊(cè)表,同時(shí)也會(huì)向其它所有服務(wù)節(jié)點(diǎn)發(fā)送請(qǐng)求,將這個(gè)服務(wù)數(shù)據(jù)同步到其它所有節(jié)點(diǎn)

所以此時(shí)從任意一個(gè)節(jié)點(diǎn)都是可以獲取到所有的服務(wù)實(shí)例數(shù)據(jù)的。

即使數(shù)據(jù)同步的過程發(fā)生異常,服務(wù)實(shí)例也成功注冊(cè)到一個(gè) Nacos 服務(wù)中,對(duì)外部而言,整個(gè) Nacos 集群是可用的,也就達(dá)到了 AP 的效果

同時(shí)為了滿足 BASE 理論,Nacos 也有下面兩種機(jī)制保證最終節(jié)點(diǎn)間數(shù)據(jù)最終是一致的:

失敗重試機(jī)制

  • 數(shù)據(jù)同步給其它節(jié)點(diǎn)失敗時(shí),會(huì)每隔 3s 重試一次,直到成功

定時(shí)對(duì)比機(jī)制

  • 每個(gè) Nacos 服務(wù)節(jié)點(diǎn)會(huì)定時(shí)向所有的其它服務(wù)節(jié)點(diǎn)發(fā)送一些認(rèn)證的請(qǐng)求
  • 這個(gè)請(qǐng)求會(huì)告訴每個(gè)服務(wù)節(jié)點(diǎn)自己負(fù)責(zé)的服務(wù)實(shí)例的對(duì)應(yīng)的版本號(hào),這個(gè)版本號(hào)隨著服務(wù)實(shí)例的變動(dòng)就會(huì)變動(dòng)
  • 如果其它服務(wù)節(jié)點(diǎn)的數(shù)據(jù)的版本號(hào)跟自己的對(duì)不上,那就說明其它服務(wù)節(jié)點(diǎn)的數(shù)據(jù)不是最新的
  • 此時(shí)這個(gè) Nacos 服務(wù)節(jié)點(diǎn)就會(huì)將自己負(fù)責(zé)的服務(wù)實(shí)例數(shù)據(jù)發(fā)給不是最新數(shù)據(jù)的節(jié)點(diǎn),這樣就保證了每個(gè)節(jié)點(diǎn)的數(shù)據(jù)是一樣的了。

Nacos 的 CP 實(shí)現(xiàn)

Nacos 的 CP 實(shí)現(xiàn)是基于 Raft 算法來實(shí)現(xiàn)

在 1.x 版本早期,Nacos 是自己手動(dòng)實(shí)現(xiàn) Raft 算法

在 2.x 版本,Nacos 移除了手動(dòng)實(shí)現(xiàn) Raft 算法,轉(zhuǎn)而擁抱基于螞蟻開源的 JRaft 框架

在 Raft 算法,每個(gè)節(jié)點(diǎn)主要有三個(gè)狀態(tài)

  • Leader,負(fù)責(zé)所有的讀寫請(qǐng)求,一個(gè)集群只有一個(gè)
  • Follower,從節(jié)點(diǎn),主要是負(fù)責(zé)復(fù)制 Leader 的數(shù)據(jù),保證數(shù)據(jù)的一致性
  • Candidate,候選節(jié)點(diǎn),最終會(huì)變成 Leader 或者 Follower

集群啟動(dòng)時(shí)都是節(jié)點(diǎn) Follower,經(jīng)過一段時(shí)間會(huì)轉(zhuǎn)換成 Candidate 狀態(tài),再經(jīng)過一系列復(fù)雜的選擇算法,選出一個(gè) Leader

當(dāng)有寫請(qǐng)求時(shí),如果請(qǐng)求的節(jié)點(diǎn)不是 Leader 節(jié)點(diǎn)時(shí),會(huì)將請(qǐng)求轉(zhuǎn)給 Leader 節(jié)點(diǎn),由 Leader 節(jié)點(diǎn)處理寫請(qǐng)求

比如,有個(gè)客戶端連到的上圖中的Nacos服務(wù)2節(jié)點(diǎn),之后向Nacos服務(wù)2注冊(cè)服務(wù)

Nacos服務(wù)2接收到請(qǐng)求之后,會(huì)判斷自己是不是 Leader 節(jié)點(diǎn),發(fā)現(xiàn)自己不是

此時(shí)Nacos服務(wù)2就會(huì)向 Leader 節(jié)點(diǎn)發(fā)送請(qǐng)求,Leader 節(jié)點(diǎn)接收到請(qǐng)求之后,會(huì)處理服務(wù)注冊(cè)的過程

為什么說 Raft 是保證 CP 的呢?

主要是因?yàn)?Raft 在處理寫的時(shí)候有一個(gè)判斷過程

  • 首先,Leader 在處理寫請(qǐng)求時(shí),不會(huì)直接數(shù)據(jù)應(yīng)用到自己的系統(tǒng),而是先向所有的 Follower 發(fā)送請(qǐng)求,讓他們先處理這個(gè)請(qǐng)求
  • 當(dāng)超過半數(shù)的 Follower 成功處理了這個(gè)寫請(qǐng)求之后,Leader 才會(huì)寫數(shù)據(jù),并返回給客戶端請(qǐng)求處理成功
  • 如果超過一定時(shí)間未收到超過半數(shù)處理成功 Follower 的信號(hào),此時(shí) Leader 認(rèn)為這次寫數(shù)據(jù)是失敗的,就不會(huì)處理寫請(qǐng)求,直接返回給客戶端請(qǐng)求失敗
小細(xì)節(jié)需要注意

Nacos 在處理查詢服務(wù)實(shí)例的請(qǐng)求直接時(shí),并不會(huì)將請(qǐng)求轉(zhuǎn)發(fā)給 Leader 節(jié)點(diǎn)處理,而是直接查當(dāng)前 Nacos 服務(wù)實(shí)例的注冊(cè)表

這其實(shí)就會(huì)引發(fā)一個(gè)問題

如果客戶端查詢的 Follower 節(jié)點(diǎn)沒有及時(shí)處理 Leader 同步過來的寫請(qǐng)求(過半響應(yīng)的節(jié)點(diǎn)中不包括這個(gè)節(jié)點(diǎn)),此時(shí)在這個(gè) Follower 其實(shí)是查不到最新的數(shù)據(jù)的,這就會(huì)導(dǎo)致數(shù)據(jù)的不一致

所以說,雖然 Raft 協(xié)議規(guī)定要求從 Leader 節(jié)點(diǎn)查最新的數(shù)據(jù),但是 Nacos 至少在讀服務(wù)實(shí)例數(shù)據(jù)時(shí)并沒有遵守這個(gè)協(xié)議

當(dāng)然對(duì)于其它的一些數(shù)據(jù)的讀寫請(qǐng)求有的還是遵守了這個(gè)協(xié)議。

數(shù)據(jù)模型

在 Nacos 中,一個(gè)服務(wù)的確定是由三部分信息確定

  • 命名空間(Namespace):多租戶隔離用的,默認(rèn)是public
  • 分組(Group):這個(gè)其實(shí)可以用來做環(huán)境隔離,服務(wù)注冊(cè)時(shí)可以指定服務(wù)的分組,比如是測(cè)試環(huán)境或者是開發(fā)環(huán)境,默認(rèn)是DEFAULT_GROUP
  • 服務(wù)名(ServiceName):這個(gè)就不用多說了

在服務(wù)注冊(cè)和訂閱的時(shí)候,必須要指定上述三部分信息,如果不指定,Nacos 就會(huì)提供默認(rèn)的信息

不過,在 Nacos 中,在服務(wù)里面其實(shí)還是有一個(gè)集群的概念

在服務(wù)注冊(cè)的時(shí)候,可以指定這個(gè)服務(wù)實(shí)例在哪個(gè)集體的集群中,默認(rèn)是在DEFAULT集群下

在 SpringCloud 環(huán)境底下可以通過如下配置去設(shè)置

spring  
    cloud:    
        nacos:      
            discovery:        
                cluster-name: sanyoujavaCluster

配置中心

Spring Boot 應(yīng)用
    ↓
Spring Cloud Nacos Config
    ↓
ConfigService(客戶端核心類)
    ↓
LongPollingRunnable(長輪詢?nèi)蝿?wù))
    ↓
HTTP 請(qǐng)求 /nacos/v1/cs/configs/listener
    ↓
Nacos Server 檢測(cè)配置變化
    ↓
返回新配置 → 客戶端觸發(fā) Listener → 事件驅(qū)動(dòng)更新 Bean

Nacos 配置中心是支持配置項(xiàng)自動(dòng)刷新的,而其實(shí)現(xiàn)的原理是通過輪詢+事件驅(qū)動(dòng)+本地回調(diào)機(jī)制的方式來實(shí)現(xiàn)的,具體來說:

  • 客戶端向 Nacos 服務(wù)器發(fā)送一個(gè)帶有監(jiān)聽器(Listener)的請(qǐng)求,以獲取某個(gè)特定配置的值。
  • Nacos 服務(wù)器接收到請(qǐng)求后,會(huì)檢查該配置是否發(fā)生了變化。如果沒有變化,則該請(qǐng)求將被阻塞,直到超時(shí)或配置發(fā)生變化。
  • 當(dāng)配置發(fā)生變化時(shí),Nacos 服務(wù)器會(huì)立即響應(yīng),并將新的配置值返回給客戶端。
  • 客戶端接收到新的配置值后,可以根據(jù)需要更新自身的配置。

長輪詢:服務(wù)器端接收到客戶端的請(qǐng)求之后,如果沒有數(shù)據(jù)更新,則連接保持一段時(shí)間,直到有數(shù)據(jù)或者超時(shí)才會(huì)返回。

gRPC 長連接是 Nacos 2.x 的推薦通信方式,性能更優(yōu),但為保證兼容性、適配多語言客戶端和輕量場景,Nacos 仍然保留了 HTTP 長輪詢機(jī)制。兩者可以共存,動(dòng)態(tài)選擇,適配更廣泛的實(shí)際業(yè)務(wù)場景。

機(jī)制說明
長輪詢保證實(shí)時(shí)監(jiān)聽變化,服務(wù)端主動(dòng)推送
客戶端本地緩存保證容錯(cuò)、降級(jí)能力
Bean 自動(dòng)刷新與 Spring 深度集成,支持注解級(jí)別的動(dòng)態(tài)刷新
異步監(jiān)聽線程保證主線程業(yè)務(wù)不被阻塞

如何集成 Nacos Config 實(shí)現(xiàn)配置項(xiàng)動(dòng)態(tài)刷新?

使用 @NacosValue 注解注入配置

import com.alibaba.nacos.api.config.annotation.NacosValue;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Component
public class NacosValueExample {

    @NacosValue(value = "${my.config.value}", autoRefreshed = true)
    private String configValue;

    @PostConstruct
    public void init() {
        System.out.println("讀取到配置:" + configValue);
    }

    public String getConfigValue() {
        return configValue;
    }
}

使用 @NacosConfigListener 手動(dòng)監(jiān)聽配置變更

import com.alibaba.nacos.api.config.annotation.NacosConfigListener;
import org.springframework.stereotype.Component;

@Component
public class NacosListenerExample {

    @NacosConfigListener(dataId = "my-config.yaml", groupId = "DEFAULT_GROUP")
    public void onChange(String newConfig) {
        System.out.println("配置發(fā)生變化,新內(nèi)容為:" + newConfig);
        // 你可以在這里解析 YAML 并手動(dòng)更新 Bean 或緩存
    }
}

使用 @ConfigurationProperties + @RefreshScope

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

@Component
@RefreshScope
@ConfigurationProperties(prefix = "my.config")
public class ConfigPropertiesExample {

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

使用 bootstrap.yml 配置動(dòng)態(tài)讀取 Nacos 的配置

# bootstrap.yml
spring:
  application:
    name: nacos-demo  # 用作默認(rèn)的 dataId:nacos-demo.yaml
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: DEFAULT_GROUP
        namespace: public
        refresh-enabled: true     # 開啟全局自動(dòng)刷新
        extension-configs:
          - data-id: my-config.yaml
            group: DEFAULT_GROUP
            refresh: true        # 支持動(dòng)態(tài)刷新
配置項(xiàng)作用
server-addrNacos 服務(wù)地址
file-extension默認(rèn) dataId 后綴,比如 nacos-demo.yaml
refresh-enabled全局開啟動(dòng)態(tài)刷新
extension-configs可以加載多個(gè)額外配置文件
refresh: true為該配置開啟動(dòng)態(tài)刷新

Spring Boot 啟動(dòng)階段讀取 bootstrap.yml,初始化 Spring Cloud Nacos。

從配置中心讀取 dataId 對(duì)應(yīng)的配置(如 my-config.yaml),并注入到環(huán)境中。

如果設(shè)置了 refresh: true,則在配置變更時(shí),Nacos 會(huì)通過監(jiān)聽機(jī)制自動(dòng)刷新對(duì)應(yīng)的 Bean。

  • 如果你用的是 @RefreshScope@NacosValue(autoRefreshed = true),對(duì)應(yīng)字段會(huì)自動(dòng)更新。
  • 也可以通過 /actuator/refresh 手動(dòng)刷新(Spring Cloud 原生方式)。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • springmvc 參數(shù)綁定總結(jié)

    springmvc 參數(shù)綁定總結(jié)

    本篇文章主要介紹了詳解springmvc 參數(shù)綁定,詳細(xì)的介紹了springmvc各種參數(shù)綁定的情況,具有一定的參考價(jià)值,有興趣的可以了解一下。
    2017-03-03
  • ElasticSearch6.2.3+head插件安裝的方法步驟

    ElasticSearch6.2.3+head插件安裝的方法步驟

    這篇文章主要介紹了ElasticSearch6.2.3+head插件安裝的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • SpringBoot新手入門的快速教程

    SpringBoot新手入門的快速教程

    這篇文章主要給大家介紹了關(guān)于SpringBoot新手入門的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用SpringBoot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 解決idea更新maven倉庫的圖文教程

    解決idea更新maven倉庫的圖文教程

    這篇文章主要介紹了解決idea更新maven倉庫的圖文教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • yaml文件中${}語法取值方式

    yaml文件中${}語法取值方式

    在Spring Boot中,配置文件中的${test.aa}等占位符的值可以通過系統(tǒng)屬性或依賴的其他模塊來獲取,這意味著,可以通過JVM參數(shù)或者系統(tǒng)屬性來指定這些值,例如,通過在啟動(dòng)命令中添加-Dtest.aa=your_value或在代碼中通過
    2024-10-10
  • java中PreparedStatement和Statement詳細(xì)講解

    java中PreparedStatement和Statement詳細(xì)講解

    這篇文章主要介紹了java中PreparedStatement和Statement詳細(xì)講解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • 通過自定制LogManager實(shí)現(xiàn)程序完全自定義的logger

    通過自定制LogManager實(shí)現(xiàn)程序完全自定義的logger

    本章主要闡述怎么完全定制化LogManager來實(shí)現(xiàn)應(yīng)用程序完全自定制的logger,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • 詳解SpringBoot中關(guān)于%2e的Trick

    詳解SpringBoot中關(guān)于%2e的Trick

    這篇文章主要介紹了SpringBoot中關(guān)于%2e的Trick,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Spring MVC的項(xiàng)目準(zhǔn)備和連接建立方法

    Spring MVC的項(xiàng)目準(zhǔn)備和連接建立方法

    SpringWebMVC是基于Servlet API的Web框架,屬于Spring框架的一部分,主要用于簡化Web應(yīng)用程序的開發(fā),SpringMVC通過控制器接收請(qǐng)求,使用模型處理數(shù)據(jù),并通過視圖展示結(jié)果,感興趣的朋友跟隨小編一起看看吧
    2024-10-10
  • java九種分布式ID解決方案

    java九種分布式ID解決方案

    在日常的業(yè)務(wù)開發(fā)中,通常需要對(duì)一些數(shù)據(jù)做唯一標(biāo)識(shí),本文主要介紹了java九種分布式ID解決方案,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09

最新評(píng)論