文章很長,而且持續更新,建議收藏起來,慢慢讀!瘋狂創客圈總目錄 博客園版 為您奉上珍貴的學習資源 :
免費贈送 :《尼恩Java面試寶典》 持續更新+ 史上最全 + 面試必備 2000頁+ 面試必備 + 大廠必備 +漲薪必備
免費贈送 經典圖書:《Java高並發核心編程(卷1)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高並發核心編程(卷2)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高並發核心編程(卷3)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《尼恩Java面試寶典 最新版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 資源寶庫: Java 必備 百度網盤資源大合集 價值>10000元 加尼恩領取
nacos高可用 (史上最全 + 圖解+秒懂)
背景:
下一個視頻版本,從架構師視角,尼恩為大家打造高可用、高並發中間件的原理與實操。
目標:通過視頻和博客的方式,為各位潛力架構師,徹底介紹清楚架構師必須掌握的高可用、高並發環境,包括但不限於:
-
高可用、高並發nginx架構的原理與實操
-
高可用、高並發mysql架構的原理與實操
-
高可用、高並發nacos架構的原理與實操
-
高可用、高並發rocketmq架構的原理與實操
-
高可用、高並發es架構的原理與實操
-
高可用、高並發minio架構的原理與實操

why 高可用、高並發中間件的原理與實操:
實際的開發過程中,很多小伙伴聚焦crud開發,環境出了問題,都不能啟動。
作為架構師,或者未來想走向高端開發,或者做架構,必須掌握高可用、高並發中間件的原理,掌握其實操。
本系列博客的具體內容,請參見 Java 高並發 發燒友社群:瘋狂創客圈
Nacos 高可用介紹
當我們在聊高可用時,我們在聊什么?
- 系統可用性達到 99.99%
- 在分布式系統中,部分節點宕機,依舊不影響系統整體運行
- 服務端集群化部署多個節點
Nacos 高可用,則是 Nacos 為了提升系統穩定性而采取的一系列手段。
Nacos 的高可用不僅僅存在於服務端,同時也存在於客戶端,以及一些與可用性相關的功能特性中,這些點組裝起來,共同構成了 Nacos 的高可用。

客戶端高可用
先統一一下語義,在微服務架構中一般會有三個角色:
-
Consumer
-
Provider
-
Registry
以上的registry 角色是 nacos-server,而 Consumer 角色和 Provider 角色都是 nacos-client。

客戶端高可用的方式一:配置多個nacos-server
在生產環境,我們往往需要搭建 Nacos 集群,代碼中,是這樣配置的:
server:
port: 8081
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848,127.0.0.1:8848,127.0.0.1:8848
當其中一台Nacos server機器宕機時,為了不影響整體運行,客戶端會存在重試機制。
package com.alibaba.nacos.client.naming.net;
/**
* @author nkorange
*/
public class NamingProxy {
//api注冊
public String reqAPI(String api, Map<String, String> params, String body, List<String> servers, String method) throws NacosException {
params.put(CommonParams.NAMESPACE_ID, getNamespaceId());
if (CollectionUtils.isEmpty(servers) && StringUtils.isEmpty(nacosDomain)) {
throw new NacosException(NacosException.INVALID_PARAM, "no server available");
}
NacosException exception = new NacosException();
if (servers != null && !servers.isEmpty()) {
Random random = new Random(System.currentTimeMillis());
int index = random.nextInt(servers.size());
//拿到地址列表,在請求成功之前逐個嘗試,直到成功為止
for (int i = 0; i < servers.size(); i++) {
String server = servers.get(index);
try {
return callServer(api, params, body, server, method);
} catch (NacosException e) {
exception = e;
if (NAMING_LOGGER.isDebugEnabled()) {
NAMING_LOGGER.debug("request {} failed.", server, e);
}
}
index = (index + 1) % servers.size();
}
}
...
該可用性保證存在於 nacos-client 端。
Nacos Java Client通用參數
| 參數名 | 含義 | 可選值 | 默認值 | 支持版本 |
|---|---|---|---|---|
| endpoint | 連接Nacos Server指定的連接點,可以參考文檔 | 域名 | 空 | >= 0.1.0 |
| endpointPort | 連接Nacos Server指定的連接點端口,可以參考文檔 | 合法端口號 | 空 | >= 0.1.0 |
| namespace | 命名空間的ID | 命名空間的ID | config模塊為空,naming模塊為public | >= 0.8.0 |
| serverAddr | Nacos Server的地址列表,這個值的優先級比endpoint高 | ip:port,ip:port,... | 空 | >= 0.1.0 |
| JM.LOG.PATH(-D) | 客戶端日志的目錄 | 目錄路徑 | 用戶根目錄 | >= 0.1.0 |
客戶端高可用的方式二:本地緩存文件 Failover 機制
注冊中心發生故障最壞的一個情況是整個 Server 端宕機,如果三個Server 端都宕機了,怎么辦呢?
這時候 Nacos 依舊有高可用機制做兜底。
本地緩存文件 Failover 機制
一道經典的 高可用的面試題:
當 springcloud 應用運行時,Nacos 注冊中心宕機,會不會影響 RPC 調用。
這個題目大多數人,應該都不能回答出來.
Nacos 存在本地文件緩存機制,nacos-client 在接收到 nacos-server 的服務推送之后,會在內存中保存一份,隨后會落盤存儲一份快照snapshot 。有了這份快照,本地的RPC調用,還是能正常的進行。
關鍵是,這個本地文件緩存機制,默認是關閉的。
Nacos 注冊中心宕機,Dubbo /springcloud 應用發生重啟,會不會影響 RPC 調用。如果了解了 Nacos 的 Failover 機制,應當得到和上一題同樣的回答:不會。
客戶端Naming通用參數
| 參數名 | 含義 | 可選值 | 默認值 | 支持版本 |
|---|---|---|---|---|
| namingLoadCacheAtStart | 啟動時是否優先讀取本地緩存 | true/false | false | >= 1.0.0 |
| namingClientBeatThreadCount | 客戶端心跳的線程池大小 | 正整數 | 機器的CPU數的一半 | >= 1.0.0 |
| namingPollingThreadCount | 客戶端定時輪詢數據更新的線程池大小 | 正整數 | 機器的CPU數的一半 | >= 1.0.0 |
| com.alibaba.nacos.naming.cache.dir(-D) | 客戶端緩存目錄 | 目錄路徑 | {user.home}/nacos/naming | >= 1.0.0 |
| com.alibaba.nacos.naming.log.level(-D) | Naming客戶端的日志級別 | info,error,warn等 | info | >= 1.0.0 |
| com.alibaba.nacos.client.naming.tls.enable(-D) | 是否打開HTTPS | true/false | false |
snapshot 默認的存儲路徑為:{USER_HOME}/nacos/naming/ 中:
這份文件有兩種價值,一是用來排查服務端是否正常推送了服務;二是當客戶端加載服務時,如果無法從服務端拉取到數據,會默認從本地文件中加載。
在生產環境,推薦開啟該參數,以避免注冊中心宕機后,導致服務不可用,在服務注冊發現場景,可用性和一致性 trade off 時,我們大多數時候會優先考慮可用性。
另外:{USER_HOME}/nacos/naming/{namespace} 下除了緩存文件之外還有一個 failover 文件夾,里面存放着和 snapshot 一致的文件夾。
這是 Nacos 的另一個 failover 機制,snapshot 是按照某個歷史時刻的服務快照恢復恢復,而 failover 中的服務可以人為修改,以應對一些極端場景。
該可用性保證存在於 nacos-client 端。
Nacos兩種健康檢查模式
agent上報模式
客戶端(注冊在nacos上的其它微服務實例)健康檢查。
客戶端通過心跳上報方式告知服務端(nacos注冊中心)健康狀態;
默認心跳間隔5秒;
nacos會在超過15秒未收到心跳后將實例設置為不健康狀態;
超過30秒將實例刪除;
服務端主動檢測
服務端健康檢查。
nacos主動探知客戶端健康狀態,默認間隔為20秒;
健康檢查失敗后實例會被標記為不健康,不會被立即刪除。
臨時實例
臨時實例通過agent上報模式實現健康檢查。
Nacos 在 1.0.0版本 instance級別增加了一個ephemeral字段,該字段表示注冊的實例是否是臨時實例還是持久化實例。
微服務注冊為臨時實例:
# 默認true
spring:
cloud:
nacos:
discovery:
ephemeral: true
注意: 默認為臨時實例,表示為臨時實例。

注冊實例支持ephemeral字段
如果是臨時實例,則instance不會在 Nacos 服務端持久化存儲,需要通過上報心跳的方式進行包活,
如果instance一段時間內沒有上報心跳,則會被 Nacos 服務端摘除。
在被摘除后如果又開始上報心跳,則會重新將這個實例注冊。
持久化實例則會持久化被 Nacos 服務端,此時即使注冊實例的客戶端進程不在,這個實例也不會從服務端刪除,只會將健康狀態設為不健康。
同一個服務下可以同時有臨時實例和持久化實例,這意味着當這服務的所有實例進程不在時,會有部分實例從服務上摘除,剩下的實例則會保留在服務下。
使用實例的ephemeral來判斷,ephemeral為true對應的是服務健康檢查模式中的 client 模式,為false對應的是 server 模式。
Nacos 1.0.0 之前服務的健康檢查模式有三種:client、server 和none, 分別代表客戶端上報、服務端探測和取消健康檢查。在控制台操作的位置如下所示:

在 Nacos 1.0.0 中將把這個配置去掉,改為使用實例的ephemeral來判斷,ephemeral為true對應的是服務健康檢查模式中的 client 模式,為false對應的是 server 模式。
臨時實例和持久化實例區別
臨時和持久化的區別主要在健康檢查失敗后的表現,持久化實例健康檢查失敗后會被標記成不健康,而臨時實例會直接從列表中被刪除。
這個特性比較適合那些需要應對流量突增,而彈性擴容的服務,當流量降下來后這些實例自己銷毀自己就可以了,不用再去nacos里手動調用注銷實例。持久化以后,可以實時看到健康狀態,便於做后續的告警、擴容等一系列處理。

Nacos Server運行模式
Server的運行模式,是指 Nacos Server 可以運行在多種模式下,當前支持三種模式:
- AP、
- CP
- MIXED 。
這里的運行模式,使用的是CAP理論里的C、A和P概念。
CAP原則又稱CAP定理,指的是在一個分布式系統中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。
一致性(C):在分布式系統中的所有數據備份,在同一時刻是否同樣的值。(等同於所有節點訪問同一份最新的數據副本)
可用性(A):在集群中一部分節點故障后,集群整體是否還能響應客戶端的讀寫請求。(對數據更新具備高可用性)
分區容忍性(P):以實際效果而言,分區相當於對通信的時限要求。系統如果不能在時限內達成數據一致性,就意味着發生了分區的情況,必須就當前操作在C和A之間做出選擇。
CAP原則的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。如果在某個分布式系統中數據無副本, 那么系統必然滿足強一致性條件, 因為只有獨一數據,不會出現數據不一致的情況,此時C和P兩要素具備,但是如果系統發生了網絡分區狀況或者宕機,必然導致某些數據不可以訪問,此時可用性條件就不能被滿足,即在此情況下獲得了CP系統,但是CAP不可同時滿足 。
基於CAP理論,在分布式系統中,數據的一致性、服務的可用性和網絡分區容忍性只能三者選二。一般來說分布式系統需要支持網絡分區容忍性,那么就只能在C和A里選擇一個作為系統支持的屬性。C 的准確定義應該是所有節點在同一時間看到的數據是一致的,而A的定義是所有的請求都會收到響應。
Nacos 支持 AP 和 CP 模式的切換,這意味着 Nacos 同時支持兩者一致性協議。這樣,Nacos能夠以一個注冊中心管理這些生態的服務。不過在Nacos中,AP模式和CP模式的具體含義,還需要再說明下。
AP模式為了服務的可能性而減弱了一致性,因此AP模式下只支持注冊臨時實例。AP 模式是在網絡分區下也能夠注冊實例。在AP模式下也不能編輯服務的元數據等非實例級別的數據,但是允許創建一個默認配置的服務。同時注冊實例前不需要進行創建服務的操作,因為這種模式下,服務其實降級成一個簡單的字符創標識,不在存儲任何屬性,會在注冊實例的時候自動創建。
CP模式下則支持注冊持久化實例,此時則是以 Raft 協議為集群運行模式,因此網絡分區下不能夠注冊實例,在網絡正常情況下,可以編輯服務器別的配置。改模式下注冊實例之前必須先注冊服務,如果服務不存在,則會返回錯誤。
MIXED 模式可能是一種比較讓人迷惑的模式,這種模式的設立主要是為了能夠同時支持臨時實例和持久化實例的注冊。這種模式下,注冊實例之前必須創建服務,在服務已經存在的前提下,臨時實例可以在網絡分區的情況下進行注冊。
Nacos CP/AP模式設定
使用如下請求進行Server運行模式的設定:
curl -X PUT
'$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
Nacos CP/AP模式切換
Nacos 集群默認支持的是CAP原則中的AP原則.
但是Nacos 集群可切換為CP原則,切換命令如下:
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
同時微服務的bootstrap.properties 需配置如下選項指明注冊為臨時/永久實例
AP模式不支持數據一致性,所以只支持服務注冊的臨時實例,CP模式支持服務注冊的永久實例,滿足配置文件的一致性
#false為永久實例,true表示臨時實例開啟,注冊為臨時實例
spring.cloud.nacos.discovery.ephemeral=true
AP/CP的配套一致性協議
介紹一致性模型之前,需要回顧 Nacos 中的兩個概念:臨時服務和持久化服務。
- 臨時服務(Ephemeral):臨時服務健康檢查失敗后會從列表中刪除,常用於服務注冊發現場景。
- 持久化服務(Persistent):持久化服務健康檢查失敗后會被標記成不健康,常用於 DNS 場景。
兩種模式使用的是不同的一致性協議:
-
臨時服務使用的是 Nacos 為服務注冊發現場景定制化的私有協議 distro,其一致性模型是 AP;
-
而持久化服務使用的是 raft 協議,其一致性模型是 CP。
AP模式下的distro 協議
distro 協議的工作流程如下:
- Nacos 啟動時首先從其他遠程節點同步全部數據。
- Nacos 每個節點是平等的都可以處理寫入請求,同時把新數據同步到其他節點。
- 每個節點只負責部分數據,定時發送自己負責數據的校驗值到其他節點來保持數據一致性。
如圖所示,每個節點負責一部分服務的寫入。

但每個節點都可以接收到寫入請求,這時就存在兩種情況:
- 當該節點接收到屬於該節點負責的服務時,直接寫入。
- 當該節點接收到不屬於該節點負責的服務時,將在集群內部路由,轉發給對應的節點,從而完成寫入。

讀取操作則不需要路由,因為集群中的各個節點會同步服務狀態,每個節點都會有一份最新的服務數據。
而當節點發生宕機后,原本該節點負責的一部分服務的寫入任務會轉移到其他節點,從而保證 Nacos 集群整體的可用性。

一個比較復雜的情況是,節點沒有宕機,但是出現了網絡分區,即下圖所示:

這個情況會損害可用性,客戶端會表現為有時候服務存在有時候服務不存在。
綜上,Nacos 的 distro 一致性協議可以保證在大多數情況下,集群中的機器宕機后依舊不損害整體的可用性。
Nacos 有兩個一致性協議:distro 和 raft,distro 協議不會有腦裂問題。
CP模式下的raft協議
此文還是聚焦於介紹nacos的高可用, raft協議,請參考尼恩的架構師視頻。
集群內部的特殊的心跳同步服務
心跳機制一般廣泛存在於分布式通信領域,用於確認存活狀態。
一般心跳請求和普通請求的設計是有差異的,心跳請求一般被設計的足夠精簡,這樣在定時探測時可以盡可能避免性能下降。
而在 Nacos 中,出於可用性的考慮,一個心跳報文包含了全部的服務信息,這樣相比僅僅發送探測信息降低了吞吐量,而提升了可用性,怎么理解呢?
考慮以下的兩種場景:
- nacos-server 節點全部宕機,服務數據全部丟失。nacos-server 即使恢復運作,也無法恢復出服務,而心跳包含全部內容可以在心跳期間就恢復出服務,保證可用性。
- nacos-server 出現網絡分區。由於心跳可以創建服務,從而在極端網絡故障下,依舊保證基礎的可用性。
調用 OpenApi 依次刪除各個服務:
curl -X "DELETE mse-xxx-p.nacos-ans.mse.aliyuncs.com:8848/nacos/v1/ns/service?serviceName=providers:com.alibaba.edas.boot.EchoService:1.0.0:DUBBO&groupName=DEFAULT_GROUP"
過 5s 后刷新,服務又再次被注冊了上來,符合我們對心跳注冊服務的預期。
集群部署模式高可用
最后給大家分享的 Nacos 高可用特性來自於其部署架構。
節點數量
我們知道在生產集群中肯定不能以單機模式運行 Nacos。
那么第一個問題便是:我應該部署幾台機器?
Nacos 有兩個一致性協議:distro 和 raft,distro 協議不會有腦裂問題,所以理論來說,節點數大於等於 2 即可;raft 協議的投票選舉機制則建議是 2n+1 個節點。
綜合來看,選擇 3 個節點是起碼的,其次處於吞吐量和更吞吐量的考量,可以選擇 5 個,7 個,甚至 9 個節點的集群。
多可用區部署
組成集群的 Nacos 節點,應該盡可能考慮兩個因素:
- 各個節點之間的網絡時延不能很高,否則會影響數據同步。
- 各個節點所處機房、可用區應當盡可能分散,以避免單點故障。
以阿里雲的 ECS 為例,選擇同一個 Region 的不同可用區就是一個很好的實踐。
部署模式
生產環境,建議使用k8s部署或者阿里雲的 ECS 部署。
考慮的中等公司,都會有運維團隊,開發人員不需要參與。
所以,這里介紹的開發人員必須掌握的,docker模式的部署。
高可用nacos的部署架構

高可用nacos的部署實操
實操這塊,使用視頻介紹更為清晰,請參考尼恩的架構師視頻。
總結
本文從多個角度出發,總結了一下 Nacos 是如何保障高可用的。
高可用特性絕不是靠服務端多部署幾個節點就可以獲得的,而是要結合客戶端使用方式、服務端部署模式、使用場景綜合來考慮的一件事。
特別是在服務注冊發現場景,Nacos 為可用性做了非常多的努力,而這些保障,ZooKeeper 是不一定有的。在做注冊中心選型時,可用性保障上,Nacos 絕對是優秀的。
參考文獻:
https://nacos.io/zh-cn/docs/what-is-nacos.html
https://blog.csdn.net/qq_38826019/article/details/109433231
