Nacos 配置中心和註冊中心介紹

項目在考慮引入註冊中心和配置中心,對比了下當前較流行的配置中心和註冊中心,發現 Nacos 比較適合我們這種私有化部署比較多的場景。本文將對其介紹,後續將會從源碼實現層面進行研究分享。

一、與其他產品對比

1.1 與其他配置中心對比

當前比較流行的配置中心就是 Apollo,因此與 Apollo 進行對比。

OCq8aW

從上面可以看出,配置中心該有的功能兩者都有,Apollo 在功能上比 Nacos 更加完善。但是,Apollo 在部署上依賴的太多,運維成本較高。此外,Apollo 只是一個配置中心,Nacos 還有註冊中心,如果服務同時依賴註冊中心和配置中心,只需要部署一套 Nacos 就可以滿足需求,運維成本較低。

1.2 與其他註冊中心對比

ZPlH2y

因 Eureka 2.x 已不再迭代,不會選用。從功能上來看,Nacos 註冊中心會更加完善。從 UI 上看,Nacos 爲中文界面,更符合國人習慣。因此,Nacos 是更好的選擇。

二、Nacos 原理

2.1 系統架構

Nacos 系統架構設計如下:邏輯架構及組件:

整體架構分爲用戶層、業務層、內核層和插件,用戶層解決易用性問題,業務層解決服務發現和配置管理功能,內核層解決分佈式系統一致性、存儲、高可用等核心問題,插件解決擴展性問題。

2.2 配置中心設計

2.2.1 配置中心領域模型

一種典型使用場景如下:

2.2.2 數據一致性

配置中心在實現上採用 AP 一致性協議。

2.2.3 容災

Nacos 客戶端會在本地生成配置的快照,當客戶端無法連接到 Nacos Server 時,可以使用配置快照實現整體容災能力,類似於緩存,會在適當的時機更新,但是並沒有緩存過期的概念。

2.3 註冊中心設計

2.3.1 註冊中心數據模型

Nacos 劃分爲服務、集羣、實例三層。

      1、服務:包括以下幾個內容。

2、集羣(Cluster):一組服務實例的一個邏輯抽象的概念,介於服務和實例之間,是一部分服務屬性的下沉和實例屬性的抽象,主要保存了有關健康檢查的一些信息和數據。

3、實例(Instance):某個服務的具體提供能力的節點。

實例定義(開發運行場景)

實例元數據(運維場景)

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,配置如下:

#!/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
#!/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
#!/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 配置

  1. application.properties 中添加服務自身配置
spring.profiles.active = dev
spring.application.name = easy-console
server.port = 8096
server.servlet.context-path = /console
  1. 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 的配置需要先加載,從遠程拉取服務的配置。否則,服務的一些配置可能會先加載,導致因爲配置不存在而報錯。

  1. 服務日誌
[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]
  1. 配置中心客戶端日誌
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
  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 參數修改緩存文件的存儲位置。

4.4 Nacos 客戶端日誌

Nacos 會記錄客戶端日誌,默認目錄爲/用戶目錄,Nacos 會在默認目錄後面拼接上/logs/nacos,具體如下:

ginger@pc:~ $ tree logs
logs
└── nacos
    ├── config.log
    ├── naming.log
    └── remote.log

默認單個文件大小爲 10M,最多 7 個文件。可以在啓動腳本中添加 - D 參數修改日誌的存儲位置以及文件大小和文件數。

五、註冊中心與 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 配置

  1. application.properties 中添加服務自身配置
spring.profiles.active = dev
spring.application.name = easy-console
server.port = 8096
server.servlet.context-path = /console
  1. 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}
  1. 服務啓動日誌(最後一行)
  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )___ | '_ | '_| | '_ / _` | \ \ \ \
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |___, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: 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
  1. 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}}
  1. 其他環境啓動方式和配置中心一樣

  2. 頁面註冊效果

5.3 Consumer 配置

  1. application.properties 添加服務自身配置
spring.profiles.active = dev
spring.application.name = nacos-client
server.port = 8090
  1. 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}
  1. 獲取 Provider 服務實例
@NacosInjected
private NamingService namingService;
// 獲取服務的所有實例
namingService.getAllInstances("easy-console");
  1. 調用 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;
}
  1. 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;
    }

再次啓動服務,此時應用名已修改。

  1. 驗證下 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"}"

參考文獻

作者:景同學

來源:juejin.cn/post/7045691604153139237

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/9-IsXFWTG3qeemxHhcXy0A