1.环境准备
1.1 拉取Nacos Docker镜像
从Docker Hub拉取Nacos镜像:
docker pull nacos/nacos-server:v2.4.0
1.2 生成密钥
你可以使用命令行工具生成一个不少于32位的密钥。以下是使用 OpenSSL 生成 32 字节密钥的示例:
openssl rand -base64 32
1.3 启动Nacos容器
使用以下命令启动一个Nacos容器:
docker run -d --name nacos-server \
-e MODE=standalone \
-e NACOS_AUTH_ENABLE=true \
-e NACOS_AUTH_TOKEN=jXd7Gp8MYxJVwK/Qj3d6h5XPwTRrqJL/vope5yHd8DA= \
-e NACOS_AUTH_IDENTITY_KEY=your-identity-key \
-e NACOS_AUTH_IDENTITY_VALUE=your-identity-value \
-e NACOS_AUTH_ENABLE_USERAGENT_AUTHWHITE=true \
-p 8848:8848 \
-p 9848:9848 \
nacos/nacos-server:v2.4.0
NACOS_AUTH_ENABLE=true
启用Nacos的身份验证。
NACOS_AUTH_TOKEN
的值为正确的 Base64 编码字符串,长度不少于 32 字节。
NACOS_AUTH_IDENTITY_KEY=your-identity-key
设置身份验证的key。
NACOS_AUTH_IDENTITY_VALUE=your-identity-value
设置身份验证的value。
NACOS_AUTH_ENABLE_USERAGENT_AUTHWHITE=true
启用User Agent白名单,绕过一些安全检查。
1.4 访问Nacos控制台
在Nacos容器启动后,可以通过浏览器访问Nacos控制台,URL如下:
说明:初始化密码。
1.5 创建命名空间
操作:点击命名空间,新建命名空间。
说明:自己随便定义命名空间,随便描述。
说明:创建结果。
1.6 创建配置
1.7 创建gateway.yaml
hello: abc
说明:用来测试是否读取到配置信息,点击发布即创建。
1.8 创建gateway-dynamic-routes.yaml
spring:
cloud:
gateway:
routes:
- id: service-route
uri: lb://producer-service-1
predicates:
- Path=/service/**
filters:
- StripPrefix=1
说明:
请求匹配:当客户端发送一个HTTP请求时,Spring Cloud Gateway会根据定义的路由进行匹配。它会首先检查请求路径是否匹配Path=/service/**
这个谓词。
前缀移除:如果请求路径匹配,StripPrefix=1
过滤器会移除路径的第一个部分。例如,客户端请求/service/produce会被转换为
/produce`。
请求转发:经过前缀移除后,Gateway会根据uri
配置将请求转发到producer-service-1
服务。由于lb://
前缀的存在,Gateway会通过服务注册中心(如Eureka或Consul)找到producer-service-1
服务的一个实例,并将请求转发给该实例。
说明:测试动态路由配置,点击发布即创建。
1.9 创建结果
说明:自定义的命名空间下创建两个配置文件。
2.项目结构
说明:使用两个服务,代表不同的路由,模拟在没有重启网关服务的条件下实现,通过Nacos修改配置文件实现网关路由的配置更新与发现。
2.1 pom.xml(父类)
<?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 https://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.3.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>spring_nacos_gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>producer_service_1</module>
<module>producer_service_2</module>
</modules>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
说明:两个提供者的父类配置文件,网关模块不需要父类。
2.2 gateway模块
2.2.1 pom.xml
<?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>
<groupId>org.example</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>22</java.version>
<spring-boot.version>3.3.2</spring-boot.version>
<spring-cloud.version>2023.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2023.0.1.2</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.4.0-BETA</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba.nacos/logback-adapter -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>logback-adapter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.1.0-alpha1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.57.2</version>
</dependency>
</dependencies>
</project>
2.2.2 nacos-logback14.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_DIR" value="logs"/>
<property name="APP_NAME" value="gateway-service"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/${APP_NAME}.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/${APP_NAME}-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
</root>
</configuration>
2.2.3 bootstrap.yml
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: 192.168.186.77:8848
username: nacos
password: 123456
config:
server-addr: 192.168.186.77:8848
file-extension: yaml
group: DEFAULT_GROUP
namespace: 94507d25-b8c3-4e5c-a8ef-b02b8ce4c0fb #命名空间的ID
encode: UTF-8
username: nacos
password: 123456
gateway:
discovery:
locator:
enabled: true # 启用动态路由发现功能。
lower-case-service-id: true #将服务ID转换为小写
config:
import:
- nacos:gateway.yaml #配置文件1
- nacos:gateway-dynamic-routes.yaml #配置文件2
server:
port: 8003
说明:通过以上配置,Spring Cloud Gateway将会自动根据Nacos中的注册服务动态生成路由。例如,如果有一个服务 order-service
注册到Nacos,Gateway将自动为这个服务生成一个路由规则,将所有以 /order-service/**
开头的请求转发到该服务。
2.2.4 GatewayApplication.java
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
@LoadBalanced //启动请求的负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2.2.5 ConfigController.java
package org.example.gateway;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {
@Value("${hello:false}")
private String Hello;
@RequestMapping("/get")
public String get() {
return Hello;
}
}
2.3 producer_service_1模块
2.3.1 pom.xml
<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.example</groupId>
<artifactId>spring_nacos_gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>producer_service_1</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2023.0.1.2</version>
</dependency>
</dependencies>
</project>
2.3.2 application.yml
server:
port: 8001
spring:
application:
name: producer-service-1
cloud:
nacos:
discovery:
server-addr: http://192.168.186.77:8848
username: nacos
password: 123456
2.3.3 ProducerApplication01.java
package org.example.producer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ProducerApplication01 {
public static void main(String[] args) {
SpringApplication.run(ProducerApplication01.class, args);
}
}
2.3.4 ProducerController.java
package org.example.producer.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProducerController {
@GetMapping("/produce")
public String produce() {
return "服务提供者1号";
}
}
2.4 producer_service_2模块
说明:实际上,该部分跟producer_service_1结构完全一样,只是改了一下启动类名还有端口配置。
2.4.1 pom.xml
<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.example</groupId>
<artifactId>spring_nacos_gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>producer_service_2</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2023.0.1.2</version>
</dependency>
</dependencies>
</project>
2.4.2 application.yml
server:
port: 8002
spring:
application:
name: producer-service-2
cloud:
nacos:
discovery:
server-addr: http://192.168.186.77:8848
username: nacos
password: 123456
说明:端口跟服务名同producer_service_1模块不一样。
2.4.3 ProducerApplication02.java
package org.example.producer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ProducerApplication02 {
public static void main(String[] args) {
SpringApplication.run(ProducerApplication02.class, args);
}
}
2.4.4 ProducerController.java
package org.example.producer.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProducerController {
@GetMapping("/produce")
public String produce() {
return "服务提供者2号";
}
}
说明:启动类同producer_service_1模块不一样,方便区分。
3.测试验证
3.1 测试读取配置文件(gateway.yaml)
3.2 动态路由发现测试
解释:Spring Cloud Gateway将会自动根据Nacos中的注册服务动态生成路由。例如,如果有一个服务 order-service
注册到Nacos,Gateway将自动为这个服务生成一个路由规则,将所有以 /order-service/**
开头的请求转发到该服务,本案例的服务是producer-service-2,自行类比·。
3.2 手动修改配置重新发布(gateway-dynamic-routes.yaml)
未修改前:
访问:
修改:
访问:
4.总结
静态路由:是在配置文件或配置中心中手动定义并且不会自动改变的路由。管理员需要手动添加或更新路由配置。
动态路由:是指路由条目根据实时的网络状态或服务注册信息自动更新。使用服务发现机制,路由器能够自动感知到服务的变化,并调整路由表。
静态路由 vs 动态路由
特点 | 静态路由 | 动态路由 |
---|---|---|
配置方式 | 手动配置 | 自动配置 |
维护复杂度 | 高 | 低 |
适应网络变化 | 差 | 强 |
使用场景 | 小型、固定网络 | 大型、动态变化网络 |
依赖性 | 低(不依赖服务注册中心) | 高(依赖服务注册中心) |
在Nacos中,静态路由适用于固定的、手动管理的路由配置,而动态路由适用于自动化、高效管理的动态变化的服务路由配置。根据实际需求选择合适的路由方式可以提升系统的灵活性和可维护性。