2022年了,不会还有人不知道Nacos配置中心和注册中心吧?

项目在考虑引入注册中心和配置中心,对比了下当前较流行的配置中心和注册中心,发现Nacos比较适合我们这种私有化部署比较多的场景。本文将对其介绍,后续将会从源码实现层面进行研究分享。

一、与其他产品对比

1.1 与其他配置中心对比

当前比较流行的配置中心就是Apollo,因此与Apollo进行对比。

对比项目/配置中心ApolloNacos
高可用基于Eureka基于Raft,不需要额外中间件
多环境以项目粒度为单位,每个项目下多环境以环境粒度为单位,每个环境下多个项目
配置生效秒级别热更新生效秒级别热更新生效
版本管理自动管理自动管理
配置回滚支持支持
配置继承支持配置继承,可以有公共配置,其他都来继承,适合微服务的场景不支持
灰度发布支持支持
配置格式校验支持支持
审计支持审计日志,可以清晰查看每次操作时变更的配置条目和变更前后对比支持审计日志,仅记录了变更前的原记录,未展示变更的配置条目以及变更前后对比
权限管理以项目粒度为单位对UI登录用户/客户端调用鉴权,不支持只读/读写粒度权限控制以环境粒度为单位对UI登录用户/客户端调用鉴权,支持只读/读写粒度权限控制
监听查询支持支持
多语言Go,C++,Python,Java,.net,OpenAPIPython,Java,Nodejs,Go,OpenAPI
分布式高可用最小集群数量Config2+Admin3+Portal*2+Mysql=8Nacos*3+MySql=4
通信协议HTTPHTTP、gRPC

从上面可以看出,配置中心该有的功能两者都有,Apollo在功能上比Nacos更加完善。但是,Apollo在部署上依赖的太多,运维成本较高。此外,Apollo只是一个配置中心,Nacos还有注册中心,如果服务同时依赖注册中心和配置中心,只需要部署一套Nacos就可以满足需求,运维成本较低。

1.2 与其他注册中心对比

对比项目\注册中心NacosEurekaConsul
依赖Zookeeper
CAP模型AP+CPAPCP
伸缩性Raft选举算法性能、可用性、容错性均比较好使用广播同步信息,集群超过1000台机器后对Eureka集群压力很大Raft选举算法
健康检查方式TPC/HTTP/SQL/Client BeatClient BeatTCP/HTTP/gRPC/CMD
负载均衡策略权重/MetaData/SelectorRibbonFabio
跨中心同步支持不支持支持
版本迭代正常迭代已不升级正常迭代
集成支持SpringCloud/K8SSpringCloudSpringCloud/K8S
访问协议HTTP/DNS/gRPCHTTPHTTP/DNS
雪崩保护支持支持不支持
自动注销实例支持支持不支持
监听支持支持支持支持
界面中文英文英文

因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划分为服务、集群、实例三层。

  • 服务:包括以下几个内容。
    • 命名空间(Namespace):Nacos数据模型中最顶层的概念,可用于环境或者租户之前强隔离的场景。
    • 分组(Group):次于命名空间的一种隔离概念,属于一个弱隔离概念,主要用于逻辑区分一些服务使用场景或不同应用的同名服务,比如同一个服务的测试分组和生产分组。
    • 服务名(Name):服务实例的名字,用于描述该服务提供了某种功能或能力。
  • 集群(Cluster):一组服务实例的一个逻辑抽象的概念,介于服务和实例之间,是一部分服务属性的下沉和实例属性的抽象,主要保存了有关健康检查的一些信息和数据。
    • 健康检查类型:支持TCP,HTTP,MySQL,设置为NONE可以关闭健康检查。
    • 健康检查端口:设置用于健康检查的端口。
    • 是否使用实例端口进行健康检查:如果使用实例端口进行健康检查,将会使用实例定义中的网络端口进行健康检查,而不再使用上述设置的健康检查端口进行。
    • 拓展数据:用于用户自定义扩展的元数据内容,形式为 K-V 。
  • 实例(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配置

  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参数修改缓存文件的存储位置。

  • 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配置

  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="{

版权声明:本文为q1472750149原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。