从 0 到 1 精通 Nacos:服务发现与配置中心的实战指南
引言:为什么 Nacos 是微服务架构的必备神器?
在微服务架构盛行的今天,服务治理成为了系统设计中不可或缺的一环。想象一下,当你的系统从单体应用拆分为数十甚至上百个微服务时,如何高效地管理这些服务的注册与发现?如何在不重启服务的情况下动态调整配置?如何实现服务的负载均衡与故障转移?
Nacos(Dynamic Naming and Configuration Service)应运而生,它是阿里巴巴开源的一款集服务发现、配置管理于一体的中间件。自 2018 年开源以来,Nacos 凭借其强大的功能、优异的性能和易用性,迅速成为微服务生态中的佼佼者,被广泛应用于各类企业级系统中。
本文将带你全面深入地了解 Nacos,从核心概念到底层原理,从环境搭建到实战应用,让你真正掌握这一微服务利器。无论你是刚接触微服务的新手,还是寻求进阶的资深开发者,都能从本文中获益。
一、Nacos 核心概念与架构解析
1.1 什么是 Nacos?
Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它提供了一组简单易用的特性集,帮助开发者快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 的名字来源于以下几个词的组合:
- Naming:服务命名与发现
- Configuration:配置管理
- Service:服务管理
简单来说,Nacos 就是微服务的 "通讯录"(服务发现)和 "控制面板"(配置管理)。
1.2 Nacos 的核心功能
Nacos 主要提供两大核心功能:
-
服务发现与服务健康检查
- 支持基于 DNS 和 RPC 的服务发现
- 实时健康检查,防止向不健康的服务实例发送请求
- 支持多种健康检查方式:TCP、HTTP、MySQL 等
-
动态配置服务
- 集中式配置管理,支持动态更新
- 配置版本控制与回滚
- 配置变更监听与推送
除此之外,Nacos 还提供了服务元数据管理、流量管理等高级特性,为微服务架构提供了全方位的支持。
1.3 Nacos 架构设计
Nacos 的架构设计充分考虑了高可用性、 scalability 和灵活性,其核心架构如图所示:
Nacos 架构主要包含以下几个部分:
- 客户端(Client):集成在应用中的 Nacos 客户端,负责与服务器通信
- Nacos 服务器集群(Nacos Server Cluster):提供服务发现和配置管理的核心服务
- 存储层:
- 嵌入式存储:用于单机模式,简化部署
- 外部存储:主要是 MySQL,用于集群模式,保证数据一致性
- 核心服务:
- 配置服务(Config Service):处理配置相关的请求
- 命名服务(Naming Service):处理服务发现相关的请求
1.4 Nacos 的两种运行模式
Nacos 支持两种运行模式,以适应不同的场景需求:
-
单机模式:
- 适用于开发、测试环境
- 使用嵌入式数据库(Derby)存储数据
- 部署简单,无需额外配置数据库
-
集群模式:
- 适用于生产环境
- 必须使用外部数据库(MySQL)存储数据
- 多节点部署,提供高可用性和负载均衡
二、Nacos 环境搭建与配置
2.1 准备工作
在开始搭建 Nacos 环境之前,我们需要准备以下环境:
- JDK 17+(推荐使用 JDK 17)
- MySQL 8.0+(集群模式需要)
- Maven 3.6+(源码编译需要)
- Git(源码下载需要)
2.2 下载与安装 Nacos
2.2.1 二进制包安装(推荐)
Nacos 官方提供了预编译的二进制包,我们可以直接下载使用:
# 下载最新稳定版本(当前最新稳定版为2.3.2)
wget https://github.com/alibaba/nacos/releases/download/2.3.2/nacos-server-2.3.2.tar.gz# 解压
tar -zxvf nacos-server-2.3.2.tar.gz# 进入nacos目录
cd nacos/bin
2.2.2 源码编译安装
如果需要自定义 Nacos 或体验最新功能,可以从源码编译:
# 克隆源码仓库
git clone https://github.com/alibaba/nacos.git# 进入源码目录
cd nacos# 切换到稳定版本分支
git checkout 2.3.2# 编译打包
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U# 进入编译后的目录
cd distribution/target/nacos-server-2.3.2/nacos/bin
2.3 单机模式启动
单机模式下,Nacos 使用嵌入式数据库,无需额外配置:
# Linux/Unix/Mac系统
sh startup.sh -m standalone# Windows系统
cmd startup.cmd -m standalone
启动成功后,访问 http://localhost:8848/nacos 即可打开 Nacos 控制台,默认用户名和密码都是 nacos。
2.4 集群模式配置与启动
集群模式需要使用 MySQL 数据库,提供更高的可用性和扩展性。
2.4.1 数据库配置
- 首先创建 MySQL 数据库(推荐使用 MySQL 8.0):
CREATE DATABASE nacos_config CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 导入初始化 SQL 脚本,脚本位于 Nacos 安装目录的 conf/nacos-mysql.sql:
# 使用mysql命令导入
mysql -u root -p nacos_config < ../conf/nacos-mysql.sql
- 修改 Nacos 配置文件 conf/application.properties,添加数据库配置:
# 数据源平台 可选值:mysql/oracle/postgresql
spring.datasource.platform=mysql# 数据库数量
db.num=1# 数据库连接信息
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=your_password# 连接池配置
db.pool.config.connectionTimeout=30000
db.pool.config.validationTimeout=10000
db.pool.config.maximumPoolSize=20
db.pool.config.minimumIdle=5
2.4.2 集群配置
- 在 conf 目录下创建 cluster.conf 文件,添加所有节点的 IP 和端口:
192.168.1.101:8848
192.168.1.102:8848
192.168.1.103:8848
- 分别在每个节点上启动 Nacos:
# Linux/Unix/Mac系统
sh startup.sh# Windows系统
cmd startup.cmd
2.4.3 配置负载均衡(可选)
为了提高可用性,可以在 Nacos 集群前配置负载均衡器(如 Nginx):
upstream nacos_cluster {server 192.168.1.101:8848;server 192.168.1.102:8848;server 192.168.1.103:8848;
}server {listen 80;server_name nacos.example.com;location / {proxy_pass http://nacos_cluster;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;}
}
2.5 Nacos 控制台介绍
Nacos 提供了直观易用的 Web 控制台,主要包含以下功能模块:
- 服务管理:查看和管理注册的服务及其实例
- 配置管理:管理配置集和配置项
- 命名空间:实现配置和服务的隔离
- 集群管理:查看集群节点状态
- 权限控制:管理用户、角色和权限
登录控制台后,可以直观地看到 Nacos 的各项功能,通过图形化界面进行操作。
三、Nacos 配置中心实战
配置中心是 Nacos 的核心功能之一,它解决了分布式系统中配置管理的痛点。本节将详细介绍如何使用 Nacos 作为配置中心。
3.1 配置中心核心概念
在使用 Nacos 配置中心之前,我们需要了解几个核心概念:
- 配置项(Config Item):一个具体的配置参数及其值,如
server.port=8080
- 配置集(Config Set):一组相关或不相关的配置项的集合,通常对应一个配置文件
- 配置集 ID(Data ID):配置集的唯一标识,通常采用类文件名的命名规则,如
application.properties
- 命名空间(Namespace):用于隔离不同环境的配置,如开发、测试、生产环境
- 分组(Group):用于对配置集进行分组管理,默认分组为
DEFAULT_GROUP
3.2 在 Nacos 控制台创建配置
- 登录 Nacos 控制台,进入 "配置管理" -> "配置列表"
- 点击 "新建配置" 按钮,填写配置信息:
- Data ID:
example-service.properties
- Group:
DEFAULT_GROUP
- 配置格式:
Properties
- 配置内容:
# 服务端口 server.port=8081 # 应用名称 spring.application.name=example-service # 测试配置 app.title=Nacos Example app.description=This is a Nacos example service app.enableFeature=true app.maxConnections=100
- Data ID:
- 点击 "发布" 按钮保存配置
3.3 Spring Cloud 集成 Nacos 配置中心
下面我们将创建一个 Spring Boot 应用,集成 Nacos 配置中心,实现配置的动态获取和更新。
3.3.1 创建 Maven 项目,添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>nacos-config-example</artifactId><version>1.0.0</version><name>nacos-config-example</name><description>Nacos Config Example</description><properties><java.version>17</java.version><spring-cloud-alibaba.version>2023.0.0.0</spring-cloud-alibaba.version></properties><dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Cloud Alibaba Nacos Config --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><optional>true</optional></dependency><!-- Spring Boot Starter Test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- Swagger3 --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.3.0</version></dependency></dependencies><dependencyManagement><dependencies><!-- Spring Cloud Alibaba --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>
3.3.2 创建配置文件
创建bootstrap.properties
文件(注意:必须使用 bootstrap 而不是 application,因为配置中心的配置需要在应用启动早期加载):
# Nacos配置中心地址
spring.cloud.nacos.config.server-addr=localhost:8848# 配置文件的Data ID
spring.cloud.nacos.config.name=example-service# 配置文件的Group
spring.cloud.nacos.config.group=DEFAULT_GROUP# 配置文件的格式
spring.cloud.nacos.config.file-extension=properties# 命名空间,默认为public
# spring.cloud.nacos.config.namespace=public
3.3.3 创建配置实体类
package com.example.nacosconfigexample.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;/*** 应用配置类,用于映射Nacos中的配置* * @author ken*/
@Data
@Component
@ConfigurationProperties(prefix = "app")
@RefreshScope // 开启配置自动刷新
public class AppConfig {/*** 应用标题*/private String title;/*** 应用描述*/private String description;/*** 是否启用新功能*/private boolean enableFeature;/*** 最大连接数*/private int maxConnections;
}
3.3.4 创建控制器,使用配置
package com.example.nacosconfigexample.controller;import com.example.nacosconfigexample.config.AppConfig;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** 配置演示控制器* * @author ken*/
@RestController
@RequestMapping("/config")
@Slf4j
@RefreshScope // 开启配置自动刷新
@Tag(name = "配置演示接口", description = "用于演示Nacos配置中心功能的接口")
public class ConfigController {/*** 注入配置实体类*/@Autowiredprivate AppConfig appConfig;/*** 使用@Value注解获取配置*/@Value("${app.title:默认标题}")private String appTitle;/*** 获取应用配置* * @return 应用配置信息*/@GetMapping("/app")@Operation(summary = "获取应用配置", description = "获取从Nacos配置中心加载的应用配置信息")public AppConfig getAppConfig() {log.info("获取应用配置: {}", appConfig);return appConfig;}/*** 获取应用标题* * @return 应用标题*/@GetMapping("/title")@Operation(summary = "获取应用标题", description = "通过@Value注解获取应用标题")public String getAppTitle() {log.info("获取应用标题: {}", appTitle);return appTitle;}
}
3.3.5 创建启动类
package com.example.nacosconfigexample;import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** Nacos配置中心示例应用启动类* * @author ken*/
@SpringBootApplication
@OpenAPIDefinition(info = @Info(title = "Nacos配置中心示例API",version = "1.0.0",description = "用于演示Nacos配置中心功能的API文档")
)
public class NacosConfigExampleApplication {public static void main(String[] args) {SpringApplication.run(NacosConfigExampleApplication.class, args);}
}
3.3.6 运行与测试
- 启动 Nacos 服务器(单机模式)
- 启动 Spring Boot 应用
- 访问 Swagger UI:http://localhost:8081/swagger-ui/index.html
- 调用
/config/app
接口,查看返回的配置信息 - 在 Nacos 控制台修改配置,例如将
app.title
改为 "Updated Nacos Example" - 再次调用
/config/app
接口,观察配置是否已自动更新
3.4 配置动态刷新原理
Nacos 配置中心的动态刷新功能是如何实现的呢?让我们来深入了解其底层原理:
具体流程如下:
- 当 Nacos 服务器上的配置发生变更时,会主动推送变更通知给客户端
- Nacos 客户端接收到变更通知后,会从服务器拉取最新的配置
- 客户端触发 Spring 的
RefreshEvent
事件 @RefreshScope
注解对应的处理器捕获到该事件- 处理器会销毁当前作用域内的 Bean,并在下一次访问时重新创建
- 新创建的 Bean 会加载最新的配置信息,从而实现配置的动态更新
3.5 多环境配置管理
在实际开发中,我们通常需要区分开发、测试、生产等不同环境的配置。Nacos 提供了两种方式实现多环境配置管理:
3.5.1 使用命名空间(Namespace)隔离环境
-
在 Nacos 控制台创建命名空间:
- 开发环境:
dev
- 测试环境:
test
- 生产环境:
prod
- 开发环境:
-
在对应命名空间下创建配置
-
在应用中指定命名空间:
# 指定命名空间ID(注意是ID而不是名称)
spring.cloud.nacos.config.namespace=dev-namespace-id
3.5.2 使用配置分组(Group)区分环境
-
在 Nacos 控制台创建不同分组的配置:
- 开发环境:
DEV_GROUP
- 测试环境:
TEST_GROUP
- 生产环境:
PROD_GROUP
- 开发环境:
-
在应用中指定分组:
# 指定配置分组
spring.cloud.nacos.config.group=DEV_GROUP
3.5.3 使用配置文件后缀区分环境
-
创建不同环境的配置文件:
- 开发环境:
example-service-dev.properties
- 测试环境:
example-service-test.properties
- 生产环境:
example-service-prod.properties
- 开发环境:
-
在应用中通过
spring.profiles.active
指定环境:
# 激活的环境
spring.profiles.active=dev# 配置文件的Data ID,会自动拼接为example-service-dev.properties
spring.cloud.nacos.config.name=example-service
spring.cloud.nacos.config.file-extension=properties
3.6 配置共享与优先级
在微服务架构中,多个服务可能会共享一些配置,同时每个服务又有自己的个性化配置。Nacos 提供了灵活的配置共享机制。
3.6.1 共享配置
可以通过shared-configs
配置多个共享的配置:
# 共享配置
spring.cloud.nacos.config.shared-configs[0].data-id=common.properties
spring.cloud.nacos.config.shared-configs[0].group=COMMON_GROUP
spring.cloud.nacos.config.shared-configs[0].refresh=truespring.cloud.nacos.config.shared-configs[1].data-id=db.properties
spring.cloud.nacos.config.shared-configs[1].group=COMMON_GROUP
spring.cloud.nacos.config.shared-configs[1].refresh=true
3.6.2 扩展配置
还可以通过extension-configs
配置扩展配置,与共享配置类似,但优先级更高:
# 扩展配置
spring.cloud.nacos.config.extension-configs[0].data-id=redis.properties
spring.cloud.nacos.config.extension-configs[0].group=EXT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true
3.6.3 配置优先级
Nacos 配置的优先级从高到低如下:
- 应用名 + 环境名.properties(如 example-service-dev.properties)
- 扩展配置(extension-configs)
- 共享配置(shared-configs)
- 应用名.properties(如 example-service.properties)
四、Nacos 服务发现实战
服务发现是微服务架构中的核心组件,负责管理服务的注册与发现,实现服务之间的通信。Nacos 提供了强大的服务发现功能,本节将详细介绍其使用方法。
4.1 服务发现核心概念
在使用 Nacos 服务发现之前,我们需要了解几个核心概念:
- 服务(Service):一个提供特定功能的应用实例集合,如用户服务、订单服务等
- 服务实例(Instance):服务的具体运行实例,一个服务可以有多个实例
- 元数据(Metadata):服务或实例的附加信息,如版本号、权重、环境等
- 健康检查(Health Check):Nacos 定期检查服务实例是否可用的机制
4.2 Spring Cloud 集成 Nacos 服务发现
下面我们将创建两个微服务:服务提供者和服务消费者,演示如何使用 Nacos 实现服务发现和调用。
4.2.1 服务提供者
1. 创建 Maven 项目,添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>nacos-provider-example</artifactId><version>1.0.0</version><name>nacos-provider-example</name><description>Nacos Service Provider Example</description><properties><java.version>17</java.version><spring-cloud-alibaba.version>2023.0.0.0</spring-cloud-alibaba.version></properties><dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Cloud Alibaba Nacos Discovery --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><optional>true</optional></dependency><!-- Spring Boot Starter Test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- Swagger3 --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.3.0</version></dependency></dependencies><dependencyManagement><dependencies><!-- Spring Cloud Alibaba --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>
2. 创建配置文件 application.properties
# 服务端口
server.port=8081# 服务名称(服务发现的关键标识)
spring.application.name=user-service# Nacos服务发现地址
spring.cloud.nacos.discovery.server-addr=localhost:8848# 服务元数据(可选)
spring.cloud.nacos.discovery.metadata.version=1.0
spring.cloud.nacos.discovery.metadata.weight=10
spring.cloud.nacos.discovery.metadata.env=dev# 开启服务注册与发现
spring.cloud.nacos.discovery.enabled=true
3. 创建实体类 User
package com.example.nacosproviderexample.entity;import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;/*** 用户实体类* * @author ken*/
@Data
@Schema(description = "用户实体")
public class User {/*** 用户ID*/@Schema(description = "用户ID")private Long id;/*** 用户名*/@Schema(description = "用户名")private String username;/*** 用户年龄*/@Schema(description = "用户年龄")private Integer age;/*** 用户邮箱*/@Schema(description = "用户邮箱")private String email;
}
4. 创建控制器 UserController
package com.example.nacosproviderexample.controller;import com.example.nacosproviderexample.entity.User;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** 用户服务控制器* * @author ken*/
@RestController
@RequestMapping("/users")
@Slf4j
@Tag(name = "用户服务接口", description = "提供用户相关操作的接口")
public class UserController {/*** 模拟数据库存储用户信息*/private static final Map<Long, User> USER_MAP = new ConcurrentHashMap<>();/*** 当前服务端口*/@Value("${server.port}")private String serverPort;static {// 初始化测试数据User user1 = new User();user1.setId(1L);user1.setUsername("张三");user1.setAge(25);user1.setEmail("zhangsan@example.com");USER_MAP.put(1L, user1);User user2 = new User();user2.setId(2L);user2.setUsername("李四");user2.setAge(30);user2.setEmail("lisi@example.com");USER_MAP.put(2L, user2);}/*** 根据ID查询用户* * @param id 用户ID* @return 用户信息*/@GetMapping("/{id}")@Operation(summary = "根据ID查询用户", description = "根据用户ID查询用户详细信息")public User getUserById(@Parameter(description = "用户ID", required = true)@PathVariable Long id) {log.info("查询用户信息,ID: {}, 服务端口: {}", id, serverPort);User user = USER_MAP.get(id);return user;}/*** 创建用户* * @param user 用户信息* @return 创建的用户信息*/@PostMapping@Operation(summary = "创建用户", description = "创建新用户并返回用户信息")public User createUser(@Parameter(description = "用户信息", required = true)@RequestBody User user) {log.info("创建用户,用户信息: {}, 服务端口: {}", user, serverPort);USER_MAP.put(user.getId(), user);return user;}/*** 获取服务信息* * @return 服务信息*/@GetMapping("/service-info")@Operation(summary = "获取服务信息", description = "获取当前服务的信息,用于测试负载均衡")public Map<String, String> getServiceInfo() {Map<String, String> info = new HashMap<>(2);info.put("serviceName", "user-service");info.put("serverPort", serverPort);log.info("获取服务信息: {}", info);return info;}
}
5. 创建启动类
package com.example.nacosproviderexample;import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** Nacos服务提供者示例应用启动类* * @author ken*/
@SpringBootApplication
@EnableDiscoveryClient // 开启服务发现客户端功能
@OpenAPIDefinition(info = @Info(title = "用户服务API",version = "1.0.0",description = "用户服务的API文档")
)
public class NacosProviderExampleApplication {public static void main(String[] args) {SpringApplication.run(NacosProviderExampleApplication.class, args);}
}
4.2.2 服务消费者
1. 创建 Maven 项目,添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>nacos-consumer-example</artifactId><version>1.0.0</version><name>nacos-consumer-example</name><description>Nacos Service Consumer Example</description><properties><java.version>17</java.version><spring-cloud-alibaba.version>2023.0.0.0</spring-cloud-alibaba.version></properties><dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Cloud Alibaba Nacos Discovery --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- Spring Cloud OpenFeign --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!-- LoadBalancer --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><optional>true</optional></dependency><!-- Spring Boot Starter Test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- Swagger3 --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.3.0</version></dependency><!-- Fastjson2 --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.41</version></dependency></dependencies><dependencyManagement><dependencies><!-- Spring Cloud Alibaba --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!-- Spring Cloud --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>2023.0.0</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>
2. 创建配置文件 application.properties
# 服务端口
server.port=8082# 服务名称
spring.application.name=order-service# Nacos服务发现地址
spring.cloud.nacos.discovery.server-addr=localhost:8848# 开启服务注册与发现
spring.cloud.nacos.discovery.enabled=true
3. 创建 Feign 客户端,调用用户服务
package com.example.nacosconsumerexample.feign;import com.example.nacosconsumerexample.entity.User;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;import java.util.Map;/*** 用户服务Feign客户端* * @author ken*/
@FeignClient(name = "user-service") // 指定要调用的服务名称
public interface UserServiceFeignClient {/*** 根据ID查询用户* * @param id 用户ID* @return 用户信息*/@GetMapping("/users/{id}")@Operation(summary = "根据ID查询用户", description = "调用用户服务根据ID查询用户")@Parameters({@Parameter(name = "id", description = "用户ID", in = ParameterIn.PATH, required = true)})User getUserById(@PathVariable("id") Long id);/*** 创建用户* * @param user 用户信息* @return 创建的用户信息*/@PostMapping("/users")@Operation(summary = "创建用户", description = "调用用户服务创建新用户")User createUser(@RequestBody User user);/*** 获取服务信息* * @return 服务信息*/@GetMapping("/users/service-info")@Operation(summary = "获取服务信息", description = "调用用户服务获取服务信息")Map<String, String> getServiceInfo();
}
4. 创建控制器 OrderController
package com.example.nacosconsumerexample.controller;import com.example.nacosconsumerexample.entity.User;
import com.example.nacosconsumerexample.feign.UserServiceFeignClient;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;/*** 订单服务控制器* * @author ken*/
@RestController
@RequestMapping("/orders")
@Slf4j
@Tag(name = "订单服务接口", description = "提供订单相关操作的接口")
public class OrderController {/*** 注入用户服务Feign客户端*/@Autowiredprivate UserServiceFeignClient userServiceFeignClient;/*** 创建订单* * @param userId 用户ID* @return 订单信息*/@PostMapping@Operation(summary = "创建订单", description = "创建新订单,需要调用用户服务获取用户信息")public Map<String, Object> createOrder(@Parameter(description = "用户ID", required = true)@RequestParam Long userId) {log.info("创建订单,用户ID: {}", userId);// 调用用户服务获取用户信息User user = userServiceFeignClient.getUserById(userId);if (user == null) {log.error("用户不存在,用户ID: {}", userId);Map<String, Object> result = new HashMap<>(2);result.put("success", false);result.put("message", "用户不存在");return result;}// 模拟创建订单Map<String, Object> order = new HashMap<>(4);order.put("id", System.currentTimeMillis());order.put("userId", userId);order.put("userName", user.getUsername());order.put("product", "示例商品");log.info("订单创建成功,订单信息: {}", order);Map<String, Object> result = new HashMap<>(2);result.put("success", true);result.put("data", order);return result;}/*** 测试负载均衡* * @return 服务信息*/@GetMapping("/load-balance-test")@Operation(summary = "测试负载均衡", description = "多次调用,测试负载均衡效果")public Map<String, String> loadBalanceTest() {log.info("测试负载均衡");return userServiceFeignClient.getServiceInfo();}
}
5. 创建启动类
package com.example.nacosconsumerexample;import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;/*** Nacos服务消费者示例应用启动类* * @author ken*/
@SpringBootApplication
@EnableDiscoveryClient // 开启服务发现客户端功能
@EnableFeignClients // 开启Feign客户端功能
@OpenAPIDefinition(info = @Info(title = "订单服务API",version = "1.0.0",description = "订单服务的API文档")
)
public class NacosConsumerExampleApplication {public static void main(String[] args) {SpringApplication.run(NacosConsumerExampleApplication.class, args);}
}
4.2.3 运行与测试
- 启动 Nacos 服务器(单机模式)
- 启动用户服务(user-service),可以通过修改端口号启动多个实例:
bash
# 第一个实例,端口8081 java -jar nacos-provider-example-1.0.0.jar --server.port=8081# 第二个实例,端口8083 java -jar nacos-provider-example-1.0.0.jar --server.port=8083
- 启动订单服务(order-service)
- 访问 Nacos 控制台的服务管理,可以看到注册的服务和实例
- 访问订单服务的 Swagger UI:http://localhost:8082/swagger-ui/index.html
- 多次调用
/orders/load-balance-test
接口,观察返回的端口号是否在 8081 和 8083 之间切换,验证负载均衡效果 - 调用
/orders
接口,传入 userId=1,验证服务间调用是否正常
4.3 服务发现原理
Nacos 的服务发现机制主要包含服务注册、服务发现和健康检查三个核心过程:
-
服务注册:
- 服务启动时,会将自己的地址、端口、元数据等信息发送给 Nacos Server
- Nacos Server 将这些信息存储起来,并返回注册成功的响应
-
服务发现:
- 消费者启动时,会向 Nacos Server 订阅它需要调用的服务
- Nacos Server 返回该服务的所有可用实例列表
- 消费者将实例列表缓存到本地,用于后续的服务调用
- 当服务实例发生变化(新增、下线、健康状态变化)时,Nacos Server 会主动推送变更给消费者
- 消费者收到变更通知后,更新本地缓存的实例列表
-
健康检查:
- Nacos Server 会定期向服务实例发送健康检查请求
- 服务实例返回自己的健康状态
- 如果服务实例不健康,Nacos Server 会将其标记为不健康,不再将其推荐给消费者
4.4 服务路由与负载均衡
Nacos 结合 Spring Cloud LoadBalancer 提供了服务路由和负载均衡功能。默认的负载均衡策略是轮询,我们也可以根据需要自定义负载均衡策略。
4.4.1 自定义负载均衡策略
创建一个基于权重的负载均衡策略:
package com.example.nacosconsumerexample.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;/*** 负载均衡配置* * @author ken*/
@Configuration
@Slf4j
public class LoadBalancerConfig {/*** 自定义基于权重的负载均衡器* * @param environment 环境变量* @param loadBalancerClientFactory 负载均衡客户端工厂* @return 基于权重的负载均衡器*/@Beanpublic ReactorLoadBalancer<ServiceInstance> weightLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);log.info("为服务 {} 配置基于权重的负载均衡策略", name);return new WeightedLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}
}
实现权重负载均衡器:
package com.example.nacosconsumerexample.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import reactor.core.publisher.Mono;import java.util.List;
import java.util.Random;/*** 基于权重的负载均衡器* * @author ken*/
@Slf4j
public class WeightedLoadBalancer extends org.springframework.cloud.loadbalancer.core.RandomLoadBalancer {private final String serviceId;private final Random random = new Random();/*** 构造函数* * @param serviceInstanceListSupplierProvider 服务实例列表提供者* @param serviceId 服务ID*/public WeightedLoadBalancer(ServiceInstanceListSupplier serviceInstanceListSupplierProvider, String serviceId) {super(serviceInstanceListSupplierProvider, serviceId);this.serviceId = serviceId;}/*** 选择服务实例* * @return 选中的服务实例*/@Overridepublic Mono<ServiceInstance> choose() {return getInstanceSupplier().get().next().map(this::getWeightedInstance);}/*** 根据权重选择服务实例* * @param instances 服务实例列表* @return 选中的服务实例*/private ServiceInstance getWeightedInstance(List<ServiceInstance> instances) {if (instances.isEmpty()) {log.warn("服务 {} 没有可用实例", serviceId);return null;}// 计算总权重int totalWeight = 0;for (ServiceInstance instance : instances) {totalWeight += getWeight(instance);}// 随机生成一个0到总权重之间的数int randomWeight = random.nextInt(totalWeight);// 根据权重选择实例int currentWeight = 0;for (ServiceInstance instance : instances) {currentWeight += getWeight(instance);if (currentWeight > randomWeight) {log.info("选择服务实例: {}:{},权重: {}", instance.getHost(), instance.getPort(), getWeight(instance));return instance;}}// 如果出现意外情况,默认返回第一个实例return instances.get(0);}/*** 获取服务实例的权重* * @param instance 服务实例* @return 权重值,默认为1*/private int getWeight(ServiceInstance instance) {try {String weightStr = instance.getMetadata().get("weight");if (weightStr != null) {return Integer.parseInt(weightStr);}} catch (Exception e) {log.warn("获取服务实例 {}:{} 的权重失败", instance.getHost(), instance.getPort(), e);}return 1;}
}
在启动类上指定负载均衡配置:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@LoadBalancerClient(name = "user-service", configuration = LoadBalancerConfig.class
)
public class NacosConsumerExampleApplication {// ...
}
4.5 服务健康检查
Nacos 提供了多种健康检查方式,确保只有健康的服务实例会被消费者调用:
- 客户端主动上报:服务实例定期向 Nacos Server 发送心跳,报告自己的健康状态
- 服务器主动检测:Nacos Server 定期向服务实例发送健康检查请求
可以在配置文件中自定义健康检查相关参数:
# 健康检查类型:client(客户端上报)、server(服务器检测)
spring.cloud.nacos.discovery.heart-beat-type=client# 客户端心跳间隔时间(毫秒)
spring.cloud.nacos.discovery.heart-beat-interval=5000# 服务器健康检查超时时间(毫秒)
spring.cloud.nacos.discovery.heart-beat-timeout=15000# 服务实例健康检查路径(服务器检测模式下使用)
spring.cloud.nacos.discovery.health-check-path=/actuator/health# 服务实例健康检查端口
spring.cloud.nacos.discovery.health-check-port=${server.port}# 服务实例健康检查协议
spring.cloud.nacos.discovery.health-check-http-method=GET
五、Nacos 高级特性
5.1 服务元数据
服务元数据是附加在服务或实例上的键值对信息,可以用来存储服务的额外信息,如版本号、权重、环境等。
5.1.1 配置服务元数据
可以在配置文件中配置服务实例的元数据:
# 服务元数据
spring.cloud.nacos.discovery.metadata.version=1.0
spring.cloud.nacos.discovery.metadata.weight=10
spring.cloud.nacos.discovery.metadata.env=dev
spring.cloud.nacos.discovery.metadata.region=east
5.1.2 获取服务元数据
在服务消费者中,可以通过DiscoveryClient
获取服务实例的元数据:
package com.example.nacosconsumerexample.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 服务元数据控制器* * @author ken*/
@RestController
@RequestMapping("/metadata")
@Slf4j
public class MetadataController {@Autowiredprivate DiscoveryClient discoveryClient;/*** 获取用户服务的元数据* * @return 服务实例及其元数据*/@GetMapping("/user-service")public Map<String, Object> getUserServiceMetadata() {List<ServiceInstance> instances = discoveryClient.getInstances("user-service");Map<String, Object> result = new HashMap<>(2);result.put("serviceName", "user-service");result.put("instances", instances.stream().map(instance -> {Map<String, Object> instanceInfo = new HashMap<>(4);instanceInfo.put("host", instance.getHost());instanceInfo.put("port", instance.getPort());instanceInfo.put("metadata", instance.getMetadata());return instanceInfo;}).toList());log.info("获取用户服务元数据: {}", result);return result;}
}
5.2 服务限流
Nacos 结合 Sentinel 可以实现服务限流功能,保护服务不被流量峰值击垮。
5.2.1 添加依赖
<!-- Spring Cloud Alibaba Sentinel -->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency><!-- Sentinel 数据源 -->
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
5.2.2 配置限流规则
在 Nacos 控制台添加限流规则配置:
- Data ID:
sentinel-rules.json
- Group:
DEFAULT_GROUP
- 配置内容:
[{"resource": "GET:/users/{id}","limitApp": "default","grade": 1,"count": 5,"strategy": 0,"controlBehavior": 0,"clusterMode": false}
]
5.2.3 配置应用
# Sentinel 控制台地址
spring.cloud.sentinel.transport.dashboard=localhost:8080# 限流规则数据源配置
spring.cloud.sentinel.datasource.ds1.nacos.server-addr=localhost:8848
spring.cloud.sentinel.datasource.ds1.nacos.data-id=sentinel-rules.json
spring.cloud.sentinel.datasource.ds1.nacos.group-id=DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds1.nacos.data-type=json
spring.cloud.sentinel.datasource.ds1.rule-type=flow
5.2.4 实现限流降级处理
package com.example.nacosproviderexample.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.nacosproviderexample.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** 带限流功能的用户控制器* * @author ken*/
@RestController
@RequestMapping("/users-with-sentinel")
@Slf4j
public class UserWithSentinelController {/*** 根据ID查询用户,带有限流保护* * @param id 用户ID* @return 用户信息*/@GetMapping("/{id}")@SentinelResource(value = "GET:/users/{id}",blockHandler = "getUserByIdBlockHandler")public User getUserById(@PathVariable Long id) {// 模拟查询用户User user = new User();user.setId(id);user.setUsername("示例用户");user.setAge(25);user.setEmail("example@test.com");return user;}/*** 限流降级处理方法* * @param id 用户ID* @param ex 限流异常* @return 降级返回的用户信息*/public User getUserByIdBlockHandler(Long id, BlockException ex) {log.warn("查询用户被限流,用户ID: {}", id, ex);User user = new User();user.setId(id);user.setUsername("限流保护");user.setAge(0);user.setEmail("limited@test.com");return user;}
}
5.3 Nacos 与 Spring Cloud Gateway 集成
Spring Cloud Gateway 是 Spring Cloud 生态中的网关组件,与 Nacos 结合可以实现动态路由。
5.3.1 创建网关项目,添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>nacos-gateway-example</artifactId><version>1.0.0</version><name>nacos-gateway-example</name><description>Nacos Gateway Example</description><properties><java.version>17</java.version><spring-cloud-alibaba.version>2023.0.0.0</spring-cloud-alibaba.version></properties><dependencies><!-- Spring Cloud Gateway --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!-- Spring Cloud Alibaba Nacos Discovery --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- Spring Cloud Alibaba Nacos Config --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><optional>true</optional></dependency></dependencies><dependencyManagement><dependencies><!-- Spring Cloud Alibaba --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!-- Spring Cloud --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>2023.0.0</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>
5.3.2 创建配置文件 bootstrap.properties
# 服务端口
server.port=8080# 服务名称
spring.application.name=api-gateway# Nacos配置中心地址
spring.cloud.nacos.config.server-addr=localhost:8848# Nacos服务发现地址
spring.cloud.nacos.discovery.server-addr=localhost:8848# 配置文件的Data ID
spring.cloud.nacos.config.name=gateway-routes# 配置文件的Group
spring.cloud.nacos.config.group=GATEWAY_GROUP# 配置文件的格式
spring.cloud.nacos.config.file-extension=json# 开启配置自动刷新
spring.cloud.nacos.config.refresh-enabled=true
5.3.3 在 Nacos 控制台创建网关路由配置
- Data ID:
gateway-routes.json
- Group:
GATEWAY_GROUP
- 配置内容:
{"spring": {"cloud": {"gateway": {"routes": [{"id": "user-service-route","uri": "lb://user-service","predicates": ["Path=/api/users/**"],"filters": ["StripPrefix=1"]},{"id": "order-service-route","uri": "lb://order-service","predicates": ["Path=/api/orders/**"],"filters": ["StripPrefix=1"]}]}}}
}
5.3.4 创建启动类
package com.example.nacosgatewayexample;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** Nacos网关示例应用启动类* * @author ken*/
@SpringBootApplication
@EnableDiscoveryClient
public class NacosGatewayExampleApplication {public static void main(String[] args) {SpringApplication.run(NacosGatewayExampleApplication.class, args);}
}
5.3.5 测试网关路由
- 启动 Nacos、用户服务、订单服务和网关服务
- 通过网关访问用户服务:http://localhost:8080/api/users/1
- 通过网关访问订单服务:http://localhost:8080/api/orders?userId=1
- 在 Nacos 控制台修改路由配置,测试动态路由效果
六、Nacos 生产环境部署与优化
6.1 集群部署最佳实践
在生产环境中,为了保证高可用性,Nacos 需要部署为集群模式:
-
服务器要求:
- 至少 3 台服务器(推荐 4 核 8G 以上配置)
- 服务器之间网络互通
- 时间同步
-
数据库配置:
- 使用 MySQL 主从架构或集群
- 配置连接池参数,优化数据库性能
- 定期备份数据库
-
Nacos 配置:
- 配置集群节点信息(cluster.conf)
- 调整 JVM 参数,推荐配置:
-Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m
- 配置日志滚动策略,避免磁盘空间耗尽
-
负载均衡:
- 在 Nacos 集群前部署负载均衡器(如 Nginx、SLB 等)
- 配置健康检查,自动剔除不健康节点
-
安全配置:
- 开启 Nacos 认证功能
- 配置网络访问控制,限制访问来源
- 敏感配置加密存储
6.2 性能优化
为了提高 Nacos 的性能,可以从以下几个方面进行优化:
-
JVM 优化:
- 根据服务器配置调整 JVM 参数
- 启用 G1 垃圾收集器
- 配置合适的新生代和老年代比例
-
数据库优化:
- 为 Nacos 数据库表创建合适的索引
- 调整 MySQL 配置,如
innodb_buffer_pool_size
- 定期清理过期数据
-
缓存优化:
- 调整 Nacos 本地缓存大小
- 配置合理的缓存过期时间
-
网络优化:
- 使用长连接减少连接建立开销
- 调整 TCP 参数,如
net.ipv4.tcp_tw_reuse
-
配置优化:
- 合理设置服务健康检查间隔
- 批量处理配置变更通知
6.3 监控与告警
为了及时发现和解决问题,需要对 Nacos 进行监控和告警:
-
监控指标:
- 服务注册 / 发现次数
- 配置变更次数
- 健康检查成功率
- JVM 内存使用情况
- 数据库连接池状态
-
监控工具:
- Prometheus + Grafana:收集和展示监控指标
- Spring Boot Actuator:暴露 Nacos 的监控端点
- ELK Stack:收集和分析日志
-
告警配置:
- 服务健康检查失败告警
- 内存使用率过高告警
- 数据库连接数过高告警
- 接口响应时间过长告警
七、常见问题与解决方案
7.1 服务注册失败
问题现象:服务启动后,在 Nacos 控制台看不到服务实例。
可能原因及解决方案:
-
网络问题:
- 检查 Nacos 服务器地址是否正确
- 检查防火墙是否开放 Nacos 端口(默认 8848)
- 检查服务与 Nacos 服务器之间的网络连通性
-
配置问题:
- 检查是否添加了
spring-cloud-starter-alibaba-nacos-discovery
依赖 - 检查是否添加了
@EnableDiscoveryClient
注解 - 检查
spring.cloud.nacos.discovery.enabled
是否设置为 true
- 检查是否添加了
-
版本兼容性:
- 检查 Spring Cloud Alibaba 与 Spring Boot 版本是否兼容
- 推荐使用官方文档中的版本组合
7.2 配置无法动态刷新
问题现象:在 Nacos 控制台修改配置后,应用中没有获取到最新配置。
可能原因及解决方案:
-
未开启刷新功能:
- 在需要刷新的 Bean 上添加
@RefreshScope
注解 - 检查
spring.cloud.nacos.config.refresh-enabled
是否设置为 true
- 在需要刷新的 Bean 上添加
-
配置监听问题:
- 检查配置的 Data ID、Group 是否正确
- 检查命名空间是否正确
-
缓存问题:
- 检查本地缓存是否过期
- 尝试重启应用,清除缓存
7.3 服务调用失败
问题现象:消费者调用服务提供者时失败。
可能原因及解决方案:
-
服务发现问题:
- 检查服务名称是否正确
- 检查服务是否已注册到 Nacos
- 检查服务实例是否健康
-
网络问题:
- 检查服务之间的网络连通性
- 检查服务端口是否正确
-
负载均衡问题:
- 检查是否添加了负载均衡依赖
- 检查负载均衡配置是否正确
八、总结与展望
Nacos 作为一款优秀的服务发现和配置管理中间件,为微服务架构提供了强大的支持。通过本文的介绍,我们了解了 Nacos 的核心概念、架构设计、使用方法和最佳实践。
Nacos 的主要优势包括:
- 功能丰富:集服务发现和配置管理于一体,满足微服务架构的核心需求
- 易用性高:提供简洁的 API 和直观的控制台,降低使用门槛
- 性能优异:经过阿里巴巴内部大规模验证,性能稳定可靠
- 扩展性强:支持集群部署,可根据需求横向扩展
- 生态完善:与 Spring Cloud、Dubbo 等主流框架无缝集成
随着云原生技术的发展,Nacos 也在不断演进,未来可能会在以下方面进行增强:
- 更好的云原生支持:与 Kubernetes 等容器编排平台深度集成
- 更丰富的流量管理功能:提供更细粒度的流量控制能力
- 更强的安全性:增强认证、授权和加密功能
- 更高的性能:持续优化性能,支持更大规模的服务集群
掌握 Nacos 对于微服务开发者来说至关重要,它不仅能帮助我们解决实际开发中的问题,还能让我们更深入地理解微服务架构的核心思想。希望本文能为你学习和使用 Nacos 提供有益的参考。
参考资料
- Nacos 官方文档
- Spring Cloud Alibaba 官方文档
- 《Spring Cloud Alibaba 微服务实战》
- Nacos GitHub 仓库