Nacos 配置中心和註冊中心介紹
項目在考慮引入註冊中心和配置中心,對比了下當前較流行的配置中心和註冊中心,發現 Nacos 比較適合我們這種私有化部署比較多的場景。本文將對其介紹,後續將會從源碼實現層面進行研究分享。
一、與其他產品對比
1.1 與其他配置中心對比
當前比較流行的配置中心就是 Apollo,因此與 Apollo 進行對比。
從上面可以看出,配置中心該有的功能兩者都有,Apollo 在功能上比 Nacos 更加完善。但是,Apollo 在部署上依賴的太多,運維成本較高。此外,Apollo 只是一個配置中心,Nacos 還有註冊中心,如果服務同時依賴註冊中心和配置中心,只需要部署一套 Nacos 就可以滿足需求,運維成本較低。
1.2 與其他註冊中心對比
因 Eureka 2.x 已不再迭代,不會選用。從功能上來看,Nacos 註冊中心會更加完善。從 UI 上看,Nacos 爲中文界面,更符合國人習慣。因此,Nacos 是更好的選擇。
二、Nacos 原理
2.1 系統架構
Nacos 系統架構設計如下:
整體架構分爲用戶層、業務層、內核層和插件,用戶層解決易用性問題,業務層解決服務發現和配置管理功能,內核層解決分佈式系統一致性、存儲、高可用等核心問題,插件解決擴展性問題。
-
用戶層:
OpenAPI,Console,SDK,Agent,CLI。
-
業務層:
服務管理,配置管理,元數據管理。
-
內核層:
插件機制,事件機制,日誌模塊,回調機制,尋址模式,推送通道,容量管理,流量管理,緩存機制,啓動模式,一致性協議,存儲模塊。
-
插件:
NameService,CMDB,Metrics,Trace,接入管理,用戶管理,權限管理,審計系統,通知系統。
2.2 配置中心設計
2.2.1 配置中心領域模型
-
命名空間(NameSpace):
用於進行租戶粒度的配置隔離。
不同的命名空間下,可以存在相同的 Group 或 Data ID 的配置。
Namespace 的常用場景之一是不同環境的配置的區分隔離,例如開發測試環境和生產環境的資源(如數據庫配置、限流閾值、降級開關)隔離等。
如果在沒有指定 Namespace 的情況下,默認使用 public 命名空間。
-
配置組(Group):
Nacos 中的一組配置集,是配置的維度之一,默認爲 DEFAULT_GROUP,常見的使用場景爲不同的應用或組件使用了相同的配置項。
-
配置 ID(Data ID):
Nacos 中的某個配置集的 ID,是劃分配置的維度之一,一個系統或者應用可以包含多個配置集,每個配置集都可以被一個有意義的名稱標識。
一種典型使用場景如下:
2.2.2 數據一致性
配置中心在實現上採用 AP 一致性協議。
-
對於 Server 間的一致性協議:
-
有 DB,核心是保證 Server 與 DB 之前的數據一致性。
Server 之間完全對等的,數據寫任何一個 Server,優先持久化,然後異步通知其他節點到數據庫中拉取最新配置值,並且通知寫入成功。
-
無 DB,Server 間採用 Raft 協議保證數據一致性。
-
對於 Client 與 Server 之間的一致性,通過 MD5 值檢驗是否一致,不一致就拉取最新值。
在 2.x 中,客戶端會和 Server 端建立一條長連接,30 秒一次長輪訓,配置變更服務端推送變更配置列表,然後 SDK 拉取配置更新。
2.2.3 容災
Nacos 客戶端會在本地生成配置的快照,當客戶端無法連接到 Nacos Server 時,可以使用配置快照實現整體容災能力,類似於緩存,會在適當的時機更新,但是並沒有緩存過期的概念。
2.3 註冊中心設計
2.3.1 註冊中心數據模型
Nacos 劃分爲服務、集羣、實例三層。
1、服務:包括以下幾個內容。
-
命名空間(Namespace):Nacos 數據模型中最頂層的概念,可用於環境或者租戶之前強隔離的場景。
-
分組(Group):次於命名空間的一種隔離概念,屬於一個弱隔離概念,主要用於邏輯區分一些服務使用場景或不同應用的同名服務,比如同一個服務的測試分組和生產分組。
-
服務名(Name):服務實例的名字,用於描述該服務提供了某種功能或能力。
2、集羣(Cluster):一組服務實例的一個邏輯抽象的概念,介於服務和實例之間,是一部分服務屬性的下沉和實例屬性的抽象,主要保存了有關健康檢查的一些信息和數據。
-
健康檢查類型:支持 TCP,HTTP,MySQL,設置爲 NONE 可以關閉健康檢查。
-
健康檢查端口:設置用於健康檢查的端口。
-
是否使用實例端口進行健康檢查:如果使用實例端口進行健康檢查,將會使用實例定義中的網絡端口進行健康檢查,而不再使用上述設置的健康檢查端口進行。
-
拓展數據:用於用戶自定義擴展的元數據內容,形式爲 K-V 。
3、實例(Instance):某個服務的具體提供能力的節點。
實例定義(開發運行場景)
-
網絡 IP 地址:該實例的 IP 地址,在 Nacos2.0 版本後支持設置爲域名。
-
網絡端口:該實例的端口信息。
-
健康狀態:通過健康檢查的手段進行維護。
-
集羣(Cluster):標示該實例歸屬於哪個邏輯集羣。
-
拓展數據 (extendData):用於用戶自定義擴展的元數據內容,形式爲 K-V。
實例元數據(運維場景)
-
權重:浮點數,範圍爲 0-10000。權重越大,分配給該實例的流量越大。
-
上線狀態:標記該實例是否接受流量,優先級大於權重和健康狀態。
-
拓展數據:不同於實例定義中的拓展數據,這個拓展數據是給予運維人員在不變動實例本身的情況下,快速地修改和新增實例的擴展數據,從而達到運維實例的作用。
2.3.2 一致性
Nacos 支持 AP 和 CP 兩種並存的一致性協議,實現上,一個是基於簡化的 Raft 的 CP 一致性,一個是基於自研協議 Distro(Gossip 改進)的 AP 一致性。
2.3.3 持久化
Nacos 實例支持兩種,臨時實例和持久化實例,兩者區分的關鍵在與健康檢查方式。臨時實例使用客戶端上報模式,而持久化實例使用服務端反向探測模式,開啓上在 1.x 時是在實例級別,2.x 之後在服務級別。
2.3.4 心跳
正如上面所說,Nacos 既支持客戶端的健康檢查,也支持服務端的健康檢查。對於臨時實例,使用心跳上報方式維持活性,發送心跳的週期默認是 5 秒,Nacos 服務端會在 15 秒沒收到心跳後將實例設置爲不健康,在 30 秒沒收到心跳時將這個臨時實例摘除。對於持久化實例,探測方式支持 TCP 端口、HTTP 返回碼檢測方式,對於一些特殊場景,也支持如 MySQL 命令來進行檢查。
Nacos 在後期也會支持用戶擴展機制,支持用戶傳入一條業務語義的請求,然後由 Nacos 去執行,做到健康檢查的定製。
2.3.5 負載均衡
負載均衡其實並不屬於註冊中心的功能,完整的流程應該是先從註冊中心獲取服務的實例列表,再根據實際需求選擇其中的部分實例或者按照一定的流量分配機制來訪問不同的服務提供者。但 Nacos 恰好反過來,服務消費者並不關心負載均衡,只關心如何正確、高效訪問服務,服務提供者則非常關注自身被訪問的流量的調配。在實現上,Nacos 將客戶端好服務端的負載均衡相結合,提供了基於健康檢查、權重、CMDB 標籤等策略。
2.4 權限設計
Nacos 在權限設計上採用 RBAC 體系,授權的資源知道命名空間,粒度很大,功能也相對較弱。
- 用戶列表
- 角色管理
- 權限管理
三、Nacos 安裝
3.1 單機模式部署
3.1.1 使用內嵌數據庫
#!/bin/bash
# 參考地址
# https://github.com/nacos-group/nacos-docker
# web訪問http://127.0.0.1:8848,賬號/密碼:nacos/nacos
# 8848爲服務的端口
# 9848爲客戶端gRPC請求服務端端口,用於客戶端向服務端發起連接和請求,固定偏移1000,即8848+1000
# 9849爲服務端gRPC請求服務端端口,用於服務間同步等,固定偏移1001,即8848+1001
# 配置參數:
# SERVER_SERVLET_CONTEXTPATH:指定上下文前綴,默認nacos
# NACOS_APPLICATION_PORT:指定端口,默認8848
# NACOS_AUTH_ENABLE:是否開啓權限,默認false
# NACOS_AUTH_TOKEN_EXPIRE_SECONDS:token失效時間,默認18000秒
# NACOS_AUTH_TOKEN:token,默認SecretKey012345678901234567890123456789012345678901234567890123456789
# NACOS_AUTH_CACHE_ENABLE:權限緩存開關,開啓後權限緩存的更新默認有15秒的延遲,默認false
# SPRING_DATASOURCE_PLATFORM:指定數據源平臺,如果使用MySQL,值配置成mysql
# MYSQL_DATABASE_NUM:指定MySQL數據庫個數,默認1
# MYSQL_SERVICE_HOST:指定數據庫主機
# MYSQL_SERVICE_PORT:指定數據庫端口
# MYSQL_SERVICE_DB_NAME:指定庫名
# MYSQL_SERVICE_DB_PARAM:指定數據庫連接參數
# MYSQL_SERVICE_USER:指定用戶名
# MYSQL_SERVICE_PASSWORD:指定密碼
# NACOS_AUTH_TOKEN_EXPIRE_SECONDS:指定Token過期時間,默認1800秒
# NACOS_SERVER_IP:多網卡情況下,指定ip或網卡
# PREFER_HOST_MODE:如果支持主機名可以使用hostname,否則使用ip,默認也是ip
# JVM_XMS:-Xms
# JVM_XMX:-Xmx
# JVM_XMN:-Xmn
# JVM_MS:-XX:MetaspaceSize
# JVM_MMS:-XX:MaxMetaspaceSize
# TOMCAT_ACCESSLOG_ENABLED:是否開始tomcat訪問日誌的記錄
# --add-host hostname 可以用來添加主機名
# -h hostname 用於指定主機名
docker run -d \
--name some-nacos \
-e MODE=standalone \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
nacos/nacos-server:2.0.3
3.1.2 使用 MySQL 數據庫
#!/bin/bash
docker run -d \
--name some-nacos-mysql \
--net common-network \
-e MODE=standalone \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=some-mysql \
-e MYSQL_SERVICE_PORT=3306 \
-e MYSQL_SERVICE_DB_NAME=nacos \
-e 'MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true' \
-e MYSQL_SERVICE_USER=nacos \
-e MYSQL_SERVICE_PASSWORD=nacos \
nacos/nacos-server:2.0.3
3.2 集羣模式部署
3.2.1 配置數據庫
從 Nacos 官網上下載發行包或者源碼,進入nacos-2.0.3/distribution/conf
目錄,執行 SQL 腳本:
source nacos-mysql.sql
3.2.2 啓動 Nacos 實例
使用一個 Docker 實例節點進行模擬,端口依次使用 8841,8842,8843,暫未使用 Swarm,配置如下:
- nacos-start-8841.sh
#!/bin/bash
docker run -d \
--name some-nacos-8841 \
--net common-network \
-h some-nacos-8841 \
-e MODE=cluster \
-e PREFER_HOST_MODE=hostname \
-e 'NACOS_SERVERS=some-nacos-8841:8841 some-nacos-8842:8842 some-nacos-8843:8843' \
-e NACOS_APPLICATION_PORT=8841 \
-p 8841:8841 \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=some-mysql \
-e MYSQL_SERVICE_PORT=3306 \
-e MYSQL_SERVICE_DB_NAME=nacos \
-e 'MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true' \
-e MYSQL_SERVICE_USER=nacos \
-e MYSQL_SERVICE_PASSWORD=nacos \
-e JVM_XMS=512m \
-e JVM_XMX=512m \
-v /Users/ginger/nacos/logs/8841:/home/nacos/logs \
nacos/nacos-server:2.0.3
- nacos-start-8842.sh
#!/bin/bash
docker run -d \
--name some-nacos-8842 \
--net common-network \
-h some-nacos-8842 \
-e MODE=cluster \
-e PREFER_HOST_MODE=hostname \
-e 'NACOS_SERVERS=some-nacos-8841:8841 some-nacos-8842:8842 some-nacos-8843:8843' \
-e NACOS_APPLICATION_PORT=8842 \
-p 8842:8842 \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=some-mysql \
-e MYSQL_SERVICE_PORT=3306 \
-e MYSQL_SERVICE_DB_NAME=nacos \
-e 'MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true' \
-e MYSQL_SERVICE_USER=nacos \
-e MYSQL_SERVICE_PASSWORD=nacos \
-e JVM_XMS=512m \
-e JVM_XMX=512m \
-v /Users/ginger/nacos/logs/8842:/home/nacos/logs \
nacos/nacos-server:2.0.3
- nacos-start-8843.sh
#!/bin/bash
docker run -d \
--name some-nacos-8843 \
--net common-network \
-h some-nacos-8843 \
-e MODE=cluster \
-e PREFER_HOST_MODE=hostname \
-e 'NACOS_SERVERS=some-nacos-8841:8841 some-nacos-8842:8842 some-nacos-8843:8843' \
-e NACOS_APPLICATION_PORT=8843 \
-p 8843:8843 \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=some-mysql \
-e MYSQL_SERVICE_PORT=3306 \
-e MYSQL_SERVICE_DB_NAME=nacos \
-e 'MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true' \
-e MYSQL_SERVICE_USER=nacos \
-e MYSQL_SERVICE_PASSWORD=nacos \
-e JVM_XMS=512m \
-e JVM_XMX=512m \
-v /Users/ginger/nacos/logs/8843:/home/nacos/logs \
nacos/nacos-server:2.0.3
啓動效果如下:
如果要查看 Leader 節點,可以點開節點元數據查看。注意,默認情況下 Nacos 分配的內存爲 2G,如果是單機多容器部署,注意要加上此環境變量的配置,否則會一直重啓。如果是多機單容器部署則不需要。
3.3 配置 Nginx 代理
可以給集羣配置一個 Nginx 代理,作爲統一入口,以便後續管理和維護。Nginx 的部署不再多說,配置如下:
location / {
proxy_pass http://nacos-server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
add_header X-Cache $upstream_cache_status;
add_header Cache-Control no-cache;
}
upstream nacos-server {
server some-nacos-8841:8841 weight=1 max_fails=2 fail_timeout=10s;
server some-nacos-8842:8842 weight=1 max_fails=2 fail_timeout=10s;
server some-nacos-8843:8843 weight=1 max_fails=2 fail_timeout=10s;
}
四、配置中心與 SpringBoot 整合
4.1 引入 Pom 配置
<nacos-config.version>0.2.10</nacos-config.version>
<!-- Nacos配置中心 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>${nacos-config.version}</version>
</dependency>
4.2 引入 Nacos 配置
- application.properties 中添加服務自身配置
spring.profiles.active = dev
spring.application.name = easy-console
server.port = 8096
server.servlet.context-path = /console
- application.properties 添加 Nacos 配置
# Nacos配置中心服務端地址
nacos.config.server-addr = 172.17.0.26:8848
# 是否開啓Nacos配置預加載
nacos.config.bootstrap.enable = true
# 是否開啓Nacos預加載日誌
nacos.config.bootstrap.log-enable = false
# 名稱空間(只可使用名稱空間Id,不可使用Name,兩者在創建時可以使用同一個名字)
nacos.config.namespace = dev
# 配置分組
nacos.config.group = DEFAULT_GROUP
# 配置項Data Id
nacos.config.data-id = ${spring.application.name}-${spring.profiles.active}.${nacos.config.type}
# 配置格式
nacos.config.type = properties
# 是否開啓自動刷新
nacos.config.auto-refresh = true
# 遠程配置是否優先於本地配置
nacos.config.remote-first = false
# 長輪詢的重試次數
nacos.config.max-retry = 3
# 長輪詢任務重試時間,單位爲毫秒
nacos.config.config-retry-time = 2000
# 長輪詢的超時時間,單位爲毫秒
nacos.config.config-long-poll-timeout = 30000
# 監聽器首次添加時拉取遠端配置
nacos.config.enable-remote-sync-config = false
注意,nacos.config.bootstrap.enable
需要開啓,Nacos 的配置需要先加載,從遠程拉取服務的配置。否則,服務的一些配置可能會先加載,導致因爲配置不存在而報錯。
- 服務日誌
[Nacos Config Boot] : The preload log configuration is enabled
. ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )___ | '_ | '_| | '_ / _` | \ \ \ \
\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |___, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.12.RELEASE)
[2021-12-19 17:24:24.261] [main] [INFO] NacosConfigPropertiesUtils.buildNacosConfigProperties:47 - nacosConfigProperties : NacosConfigProperties{serverAddr='172.17.0.26:8848', contextPath='null', encode='null', endpoint='null', namespace='null', accessKey='null', secretKey='null', ramRoleName='null', autoRefresh=true, dataId='easy-console-dev.properties', dataIds='null', group='DEFAULT_GROUP', type=PROPERTIES, maxRetry='3', configLongPollTimeout='30000', configRetryTime='2000', enableRemoteSyncConfig=false, extConfig=[], bootstrap=Bootstrap{enable=true, logEnable=true}}
[2021-12-19 17:24:24.270] [main] [INFO] ClientWorker.addCacheDataIfAbsent:384 - [config_rpc_client] [subscribe] easy-console-dev.properties+DEFAULT_GROUP
[2021-12-19 17:24:24.277] [main] [INFO] CacheData.addListener:169 - [config_rpc_client] [add-listener] ok, tenant=, dataId=easy-console-local.properties, group=DEFAULT_GROUP, cnt=1
[2021-12-19 17:24:24.285] [main] [INFO] StartupInfoLogger.logStarting:55 - Starting EasyConsoleApplication on pc.local with PID 88943
[2021-12-19 17:24:24.286] [main] [INFO] SpringApplication.logStartupProfileInfo:652 - The following profiles are active: dev
[2021-12-19 17:24:26.526] [main] [INFO] TomcatWebServer.initialize:108 - Tomcat initialized with port(s): 8096 (http)
[2021-12-19 17:24:26.534] [main] [INFO] DirectJDKLog.log:173 - Initializing ProtocolHandler ["http-nio-8096"]
[2021-12-19 17:24:26.534] [main] [INFO] DirectJDKLog.log:173 - Starting service [Tomcat]
[2021-12-19 17:24:26.534] [main] [INFO] DirectJDKLog.log:173 - Starting Servlet engine: [Apache Tomcat/9.0.46]
- 配置中心客戶端日誌
2021-12-24 14:37:09.268 INFO [main :c.a.n.c.u.ParamUtil] [settings] [req-serv] nacos-server port:8848
2021-12-24 14:37:09.271 INFO [main :c.a.n.c.u.ParamUtil] [settings] [http-client] connect timeout:1000
2021-12-24 14:37:09.273 INFO [main :c.a.n.c.u.ParamUtil] PER_TASK_CONFIG_SIZE: 3000.0
2021-12-24 14:37:09.376 INFO [main :c.a.n.c.i.CredentialWatcher] null No credential found
2021-12-24 14:37:09.408 INFO [main :c.a.n.c.c.i.Limiter] limitTime:5.0
2021-12-24 14:37:09.904 INFO [main :c.a.n.c.c.i.LocalConfigInfoProcessor] LOCAL_SNAPSHOT_PATH:logs/nacos/config
2021-12-24 14:37:10.835 INFO [com.alibaba.nacos.client.remote.worker:c.a.n.c.c.i.ClientWorker] [e12d9f10-6594-4ddc-8dc2-fc4a6d48e5a9_config-0] Connected,notify listen context...
2021-12-24 14:37:10.891 INFO [main :c.a.n.c.c.u.JvmUtil] isMultiInstance:false
2021-12-24 14:37:10.906 INFO [main :c.a.n.c.c.i.ClientWorker] [config_rpc_client] [subscribe] easy-console-dev.properties+DEFAULT_GROUP+dev
2021-12-24 14:37:10.911 INFO [main :c.a.n.c.c.i.CacheData] [config_rpc_client] [add-listener] ok, tenant=dev, dataId=easy-console-local.properties, group=DEFAULT_GROUP, cnt=1
- 其他環境啓動
實現有兩種方式,第一種爲使用一套配置中心,通過啓動命令進行環境切換,如下:
-Dspring.profiles.active=online
第二種方式爲多環境配置,如下:
4.3 Nacos 緩存文件
Nacos 會在本地緩存遠程的配置,默認目錄爲/用戶目錄
,Nacos 會在默認目錄後面拼接上/nacos/config
,具體如下:
ginger@pc:~ $ tree nacos
nacos
├── config
│ └── config_rpc_client_nacos
│ └── snapshot-tenant
│ └── local
│ └── DEFAULT_GROUP
│ └── easy-console-dev.properties
└── naming
└── local
└── failover
可以在啓動腳本中添加 - D 參數修改緩存文件的存儲位置。
- JM.SNAPSHOT.PATH,指定緩存文件的存儲位置,默認
/用戶目錄
,因此實際目錄爲/${JM.SNAPSHOT.PATH}/nacos/config
。
4.4 Nacos 客戶端日誌
Nacos 會記錄客戶端日誌,默認目錄爲/用戶目錄
,Nacos 會在默認目錄後面拼接上/logs/nacos
,具體如下:
ginger@pc:~ $ tree logs
logs
└── nacos
├── config.log
├── naming.log
└── remote.log
默認單個文件大小爲 10M,最多 7 個文件。可以在啓動腳本中添加 - D 參數修改日誌的存儲位置以及文件大小和文件數。
-
JM.LOG.PATH,指定 Nacos 日誌存儲位置,默認爲
/用戶目錄
,因此實際目錄爲/${JM.LOG.PATH}/logs/nacos
。 -
JM.LOG.RETAIN.COUNT,保留的日誌文件數,默認 7 個。
-
JM.LOG.FILE.SIZE,單個日誌文件大小,默認 10M。
五、註冊中心與 SpringBoot 整合
5.1 引入 Pom 配置
<nacos-discovery.version>0.2.10</nacos-discovery.version>
<!-- Nacos註冊中心 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-discovery-spring-boot-starter</artifactId>
<version>${nacos-discovery.version}</version>
</dependency>
5.2 Provider 配置
- application.properties 中添加服務自身配置
spring.profiles.active = dev
spring.application.name = easy-console
server.port = 8096
server.servlet.context-path = /console
- application.properties 添加 Nacos 配置
# Nacos註冊中心
# 服務端地址
nacos.discovery.server-addr = 172.17.0.26:8848
# 是否開啓自動註冊
nacos.discovery.auto-register = true
# 註冊分組
nacos.discovery.register.group-name = DEFAULT_GROUP
# 註冊命名空間
nacos.discovery.namespace = ${spring.profiles.active}
- 服務啓動日誌(最後一行)
. ____ _ __ _ _
/\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )___ | '_ | '_| | '_ / _` | \ \ \ \
\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |___, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.12.RELEASE)
[2021-12-24 14:37:09.229] [main] [INFO] NacosConfigPropertiesUtils.buildNacosConfigProperties:47 - nacosConfigProperties : NacosConfigProperties{serverAddr='172.17.0.26:8848', contextPath='null', encode='null', endpoint='null', namespace='local', accessKey='null', secretKey='null', ramRoleName='null', autoRefresh=true, dataId='easy-console-dev.properties', dataIds='null', group='DEFAULT_GROUP', type=PROPERTIES, maxRetry='3', configLongPollTimeout='30000', configRetryTime='2000', enableRemoteSyncConfig=false, extConfig=[], bootstrap=Bootstrap{enable=true, logEnable=false}}
[2021-12-24 14:37:09.974] [main] [INFO] Reflections.scan:232 - Reflections took 33 ms to scan 1 urls, producing 3 keys and 6 values
[2021-12-24 14:37:09.999] [main] [INFO] Reflections.scan:232 - Reflections took 14 ms to scan 1 urls, producing 4 keys and 9 values
[2021-12-24 14:37:10.014] [main] [INFO] Reflections.scan:232 - Reflections took 12 ms to scan 1 urls, producing 3 keys and 10 values
[2021-12-24 14:37:10.140] [main] [INFO] Reflections.scan:232 - Reflections took 122 ms to scan 126 urls, producing 0 keys and 0 values
[2021-12-24 14:37:10.155] [main] [INFO] Reflections.scan:232 - Reflections took 12 ms to scan 1 urls, producing 1 keys and 5 values
[2021-12-24 14:37:10.168] [main] [INFO] Reflections.scan:232 - Reflections took 10 ms to scan 1 urls, producing 1 keys and 7 values
[2021-12-24 14:37:10.177] [main] [INFO] Reflections.scan:232 - Reflections took 7 ms to scan 1 urls, producing 2 keys and 8 values
[2021-12-24 14:37:10.270] [main] [INFO] Reflections.scan:232 - Reflections took 88 ms to scan 126 urls, producing 0 keys and 0 values
[2021-12-24 14:37:10.901] [main] [INFO] NacosConfigLoader.reqNacosConfig:169 - load config from nacos, data-id is : easy-console-dev.properties, group is : DEFAULT_GROUP
[2021-12-24 14:37:10.918] [main] [INFO] StartupInfoLogger.logStarting:55 - Starting EasyConsoleApplication on pc.local with PID 13082
[2021-12-24 14:37:10.921] [main] [INFO] SpringApplication.logStartupProfileInfo:652 - The following profiles are active: local
[2021-12-24 14:37:12.854] [main] [INFO] TomcatWebServer.initialize:108 - Tomcat initialized with port(s): 8096 (http)
[2021-12-24 14:37:12.861] [main] [INFO] DirectJDKLog.log:173 - Initializing ProtocolHandler ["http-nio-8096"]
[2021-12-24 14:37:12.861] [main] [INFO] DirectJDKLog.log:173 - Starting service [Tomcat]
[2021-12-24 14:37:12.862] [main] [INFO] DirectJDKLog.log:173 - Starting Servlet engine: [Apache Tomcat/9.0.46]
[2021-12-24 14:37:12.924] [main] [INFO] DirectJDKLog.log:173 - Initializing Spring embedded WebApplicationContext
[2021-12-24 14:37:12.925] [main] [INFO] ServletWebServerApplicationContext.prepareWebApplicationContext:285 - Root WebApplicationContext: initialization completed in 1964 ms
[2021-12-24 14:37:15.823] [main] [INFO] DirectJDKLog.log:173 - Starting ProtocolHandler ["http-nio-8096"]
[2021-12-24 14:37:15.837] [main] [INFO] TomcatWebServer.start:220 - Tomcat started on port(s): 8096 (http) with context path '/console'
[2021-12-24 14:37:15.886] [main] [INFO] NacosDiscoveryAutoRegister.onApplicationEvent:89 - Finished auto register service : easy-console, ip : 10.242.44.123, port : 8096
- Nacos 客戶端日誌
2021-12-24 14:37:14.700 INFO [main :c.a.n.c.naming] initializer namespace from System Property :null
2021-12-24 14:37:14.701 INFO [main :c.a.n.c.naming] initializer namespace from System Environment :null
2021-12-24 14:37:14.701 INFO [main :c.a.n.c.naming] initializer namespace from System Property :null
2021-12-24 14:37:15.839 INFO [main :c.a.n.c.naming] [REGISTER-SERVICE] local registering service easy-console with instance Instance{instanceId='', ip='10.242.44.123', port=8096, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='null', serviceName='null', metadata={preserved.register.source=SPRING_BOOT}}
2021-12-24 14:37:16.108 INFO [main :c.a.n.c.naming] [REGISTER-SERVICE] local registering service easy-console with instance Instance{instanceId='', ip='10.242.44.123', port=8096, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='null', serviceName='null', metadata={preserved.register.source=SPRING_BOOT}}
-
其他環境啓動方式和配置中心一樣
-
頁面註冊效果
5.3 Consumer 配置
- application.properties 添加服務自身配置
spring.profiles.active = dev
spring.application.name = nacos-client
server.port = 8090
- application.properties 添加 Nacos 配置
# Nacos註冊中心
# 服務端地址
nacos.discovery.server-addr = 172.17.0.26:8848
# 是否開啓自動註冊
nacos.discovery.auto-register = false
# 註冊分組
nacos.discovery.register.group-name = DEFAULT_GROUP
# 註冊命名空間
nacos.discovery.namespace = ${spring.profiles.active}
- 獲取 Provider 服務實例
@NacosInjected
private NamingService namingService;
// 獲取服務的所有實例
namingService.getAllInstances("easy-console");
- 調用 Provider 接口(以健康檢查接口爲例)
@Slf4j
@Component
public class HealthCheckTiming {
@NacosInjected
private NamingService namingService;
@Scheduled(cron = "0/5 * * * * ?")
public void checkHealth() {
try {
Instance instance = namingService.selectOneHealthyInstance("easy-console", true);
String url = "http://" + instance.getIp() + ":" + instance.getPort() + "/console/api/system/status";
String rsp = HttpUtil.getMethod(url);
log.info("check health, success, rsp={}", rsp);
} catch (IllegalStateException e) {
log.info("check health, failed, {}", e.getMessage());
} catch (Exception e) {
log.error("check health, failed", e);
}
}
}
使用時需要捕獲IllegalStateException
異常,從 Nacos 源碼上看,在獲取不到服務實例時,Nacos 會拋出IllegalStateException
異常。
public static List<Instance> selectAll(ServiceInfo serviceInfo) {
List<Instance> hosts = serviceInfo.getHosts();
if (CollectionUtils.isEmpty(hosts)) {
throw new IllegalStateException("no host to srv for serviceInfo: " + serviceInfo.getName());
}
return hosts;
}
- Consumer 頁面效果
我們發現展示的應用爲 unknown,看下 Nacos 源碼,Nacos 優先會讀取系統屬性中的project.name
作爲 Consumer 的應用名,如果獲取不到則通過啓動容器來獲取,仍然獲取不到則使用unknown
。因此,如果需要修改 Consumer 展示的應用名,只需要在服務啓動參數添加相應參數,比如-Dproject.name=nacos-client
。
private static final String PARAM_MARKING_PROJECT = "project.name";
private static final String PARAM_MARKING_JBOSS = "jboss.server.home.dir";
private static final String PARAM_MARKING_JETTY = "jetty.home";
private static final String PARAM_MARKING_TOMCAT = "catalina.base";
private static final String DEFAULT_APP_NAME = "unknown";
public static String getAppName() {
String appName;
appName = getAppNameByProjectName();
if (appName != null) {
return appName;
}
appName = getAppNameByServerHome();
if (appName != null) {
return appName;
}
return DEFAULT_APP_NAME;
}
private static String getAppNameByProjectName() {
return System.getProperty(PARAM_MARKING_PROJECT);
}
private static String getAppNameByServerHome() {
String serverHome = null;
if (SERVER_JBOSS.equals(getServerType())) {
serverHome = System.getProperty(PARAM_MARKING_JBOSS);
} else if (SERVER_JETTY.equals(getServerType())) {
serverHome = System.getProperty(PARAM_MARKING_JETTY);
} else if (SERVER_TOMCAT.equals(getServerType())) {
serverHome = System.getProperty(PARAM_MARKING_TOMCAT);
}
if (serverHome != null && serverHome.startsWith(LINUX_ADMIN_HOME)) {
return StringUtils.substringBetween(serverHome, LINUX_ADMIN_HOME, File.separator);
}
return null;
}
再次啓動服務,此時應用名已修改。
- 驗證下 Provider 服務上下線
將 Provider 提供的服務下線,此時沒有可用的 Provider 實例,我們看到 Consumer 端會立即感知到,此時 Nacos 會拋出no host to srv for serviceInfo: easy-console
。而後,重新上限該實例,我們可以看到 Provider 已可以正常提供服務。
[2021-12-25 16:40:40.023] [taskScheduler-2] [INFO] HealthCheckTiming.checkHealth:34 - check health, success, rsp="{"status":"online"}"
[2021-12-25 16:40:45.022] [taskScheduler-2] [INFO] HealthCheckTiming.checkHealth:34 - check health, success, rsp="{"status":"online"}"
[2021-12-25 16:40:50.004] [taskScheduler-2] [INFO] HealthCheckTiming.checkHealth:36 - check health, failed, no host to srv for serviceInfo: easy-console
[2021-12-25 16:40:55.006] [taskScheduler-1] [INFO] HealthCheckTiming.checkHealth:36 - check health, failed, no host to srv for serviceInfo: easy-console
[2021-12-25 16:41:00.001] [taskScheduler-4] [INFO] HealthCheckTiming.checkHealth:36 - check health, failed, no host to srv for serviceInfo: easy-console
[2021-12-25 16:41:05.002] [taskScheduler-4] [INFO] HealthCheckTiming.checkHealth:36 - check health, failed, no host to srv for serviceInfo: easy-console
[2021-12-25 16:41:10.006] [taskScheduler-4] [INFO] HealthCheckTiming.checkHealth:36 - check health, failed, no host to srv for serviceInfo: easy-console
[2021-12-25 16:41:15.005] [taskScheduler-4] [INFO] HealthCheckTiming.checkHealth:36 - check health, failed, no host to srv for serviceInfo: easy-console
[2021-12-25 16:41:20.006] [taskScheduler-4] [INFO] HealthCheckTiming.checkHealth:36 - check health, failed, no host to srv for serviceInfo: easy-console
[2021-12-25 16:41:25.004] [taskScheduler-2] [INFO] HealthCheckTiming.checkHealth:36 - check health, failed, no host to srv for serviceInfo: easy-console
[2021-12-25 16:41:30.004] [taskScheduler-2] [INFO] HealthCheckTiming.checkHealth:36 - check health, failed, no host to srv for serviceInfo: easy-console
[2021-12-25 16:41:35.036] [taskScheduler-3] [INFO] HealthCheckTiming.checkHealth:34 - check health, success, rsp="{"status":"online"}"
[2021-12-25 16:41:40.025] [taskScheduler-3] [INFO] HealthCheckTiming.checkHealth:34 - check health, success, rsp="{"status":"online"}"
參考文獻
-
Nacos 文檔 (https://nacos.io/zh-cn/docs/what-is-nacos.html)
-
Nacos 官方設計文檔 (https://www.yuque.com/nacos/ebook/iez8a4)
-
攜程 Apollo 配置中心架構深度剖析
作者:景同學
來源:juejin.cn/post/7045691604153139237
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/9-IsXFWTG3qeemxHhcXy0A