Docker + Nacos + Spring Cloud Gateway 实现简单的动态路由配置修改和动态路由发现

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 复制代码
<?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 复制代码
<?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 复制代码
<?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

yaml 复制代码
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

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

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

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

yaml 复制代码
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

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

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

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

yaml 复制代码
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

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

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中,静态路由适用于固定的、手动管理的路由配置,而动态路由适用于自动化、高效管理的动态变化的服务路由配置。根据实际需求选择合适的路由方式可以提升系统的灵活性和可维护性。

相关推荐
api茶飘香25 分钟前
守护应用边界:通过反射API实现安全的输入输出过滤
java·开发语言·python·安全·django·virtualenv·pygame
杀死一只知更鸟debug27 分钟前
策略模式的小记
java·开发语言·策略模式
nice6666027 分钟前
CSS的基本语法
java·前端·css·visual studio code
Lill_bin27 分钟前
报表生成---JFreeChart
docker·信息可视化·zookeeper·云原生·容器·jenkins
sco528232 分钟前
【Shiro】Shiro 的学习教程(三)之 SpringBoot 集成 Shiro
spring boot·后端·学习
ever_up9733 小时前
EasyExcel的导入与导出及在实际项目生产场景的一下应用例子
java·开发语言·数据库
小小小小关同学3 小时前
Spring Cloud LoadBalancer
后端·spring·spring cloud
ok!ko4 小时前
设计模式之工厂模式(通俗易懂--代码辅助理解【Java版】)
java·开发语言·设计模式
丷丩4 小时前
一个Java中有用的JacksonUtil类
java·json·工具
爱摄影的程序猿4 小时前
JAVA springboot面试题今日分享
java·spring boot·spring·面试