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

相关推荐
阿伟*rui30 分钟前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck2 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei2 小时前
java的类加载机制的学习
java·学习
Yaml44 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~4 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616884 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
追风林5 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
aloha_7895 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet