欢迎光临
我们一直在努力

微服务网关设计与实战:Spring Cloud Gateway全解析

第十八章 微服务网关设计与实战:Spring Cloud Gateway全解析

在这里插入图片描述

一、章节学习目标与重点

1.1 学习目标

  • 理解微服务网关的核心价值、应用场景及主流网关技术对比,掌握Spring Cloud Gateway的核心架构与工作原理。
  • 熟练使用Spring Cloud Gateway实现路由转发、动态路由、统一认证、限流熔断、日志监控等核心功能。
  • 能够独立设计并搭建高可用、高性能的微服务网关,解决微服务架构中的接口统一管理、安全防护等问题。
  • 掌握网关的性能优化、故障排查方法,结合实际场景进行网关架构设计与落地。

1.2 学习重点

  • 微服务网关的核心作用与Spring Cloud Gateway的核心组件(路由、断言、过滤器)。
  • 路由规则配置(静态路由、动态路由)与断言工厂的实战应用。
  • 全局过滤器与局部过滤器的开发,实现统一认证、权限校验、请求改写等功能。
  • 网关与Sentinel、Nacos的整合,实现限流、熔断、动态配置。
  • 网关的高可用部署与性能优化方案。

二、微服务网关核心概念与技术选型

2.1 微服务网关的核心价值

💡 在微服务架构中,客户端(Web/APP)需要与多个微服务直接通信,会面临接口分散、认证复杂、跨域问题、流量控制困难等挑战。微服务网关作为整个微服务架构的“入口”,统一接收客户端请求,再转发到对应的微服务,相当于“流量调度中心”和“安全防护墙”。

微服务网关的核心价值体现在以下6个方面:

  • 统一入口管理:客户端只需对接网关,无需记忆多个微服务的地址,简化客户端开发与维护。例如电商系统中,客户端通过/api/user/*、/api/order/*等统一路径访问不同服务,无需关心用户服务、订单服务的具体部署地址。
  • 路由转发:根据请求路径、请求头、参数等规则,将请求转发到对应的微服务,支持负载均衡、路径重写等功能。
  • 安全防护:集中处理认证授权、签名校验、防SQL注入、防XSS攻击等安全策略,避免每个微服务重复开发安全逻辑。
  • 流量控制:对进入系统的流量进行限流、熔断、降级,保护后端微服务不被流量峰值压垮。
  • 跨域解决方案:集中处理跨域请求(CORS),无需每个微服务单独配置跨域规则。
  • 监控与日志:统一收集请求日志、监控请求耗时、错误率等指标,便于问题排查与系统运维。
  • 2.2 主流微服务网关技术对比

    目前Java生态中主流的微服务网关有3种:Spring Cloud Gateway、Zuul、Kong,各有优劣,需根据业务场景选型:

    技术选型核心优势不足之处适用场景
    Spring Cloud Gateway 1. 基于Spring生态,与Spring Cloud无缝整合;2. 基于Netty异步非阻塞,性能优异(吞吐量是Zuul的2-3倍);3. 支持动态路由、灵活的断言与过滤器;4. 原生支持WebSocket、限流、熔断等功能 1. 仅支持Spring Boot/Spring Cloud项目;2. 学习成本略高于Zuul Spring Cloud生态的微服务项目,对性能要求较高的场景
    Zuul(1.x/2.x) 1. 简单易用,与Spring Cloud整合成熟;2. 社区成熟,文档丰富;3. Zuul 2.x支持异步非阻塞 1. Zuul 1.x基于Servlet同步阻塞,性能较差;2. 功能扩展性不如Spring Cloud Gateway 小型微服务项目,对性能要求不高,追求开发效率
    Kong 1. 基于Nginx,性能极高,支持高并发;2. 支持多语言微服务(不局限于Java);3. 丰富的插件生态(认证、限流、监控等) 1. 与Spring Cloud生态整合不够紧密;2. 配置复杂,二次开发成本高 多语言混合开发的微服务项目,对性能和并发要求极高的场景

    💡 推荐选型:Spring Cloud Gateway是目前Spring Cloud生态的首选网关,兼顾性能、易用性与扩展性,本章重点讲解其设计与实战。

    2.3 Spring Cloud Gateway核心架构

    Spring Cloud Gateway基于异步非阻塞模型(Netty+Reactor)实现,核心架构包含3个核心组件,流程如下:

    客户端请求 → 网关接收请求 → 路由匹配(断言Predicate) → 过滤器链执行(Filter) → 转发到微服务 → 微服务响应 → 过滤器链反向执行 → 响应客户端

    2.3.1 核心组件详解
  • 路由(Route):网关的基本转发规则,包含3个关键信息:

    • 路由ID(Route ID):唯一标识路由的字符串(如user-service-route)。
    • 目标URI(URI):请求最终转发到的微服务地址(如lb://user-service,lb表示负载均衡)。
    • 断言集合(Predicate):判断请求是否匹配该路由的条件(如路径匹配/api/user/**、请求方法为GET)。
    • 过滤器集合(Filter):请求转发前后执行的逻辑(如添加请求头、权限校验)。
  • 断言(Predicate):本质是“条件判断函数”,Spring Cloud Gateway内置了多种断言工厂(如路径断言、请求头断言、时间断言),支持组合使用。只有当所有断言都满足时,请求才会匹配对应的路由。

  • 过滤器(Filter):分为全局过滤器(Global Filter)和局部过滤器(Gateway Filter):

    • 局部过滤器:仅对特定路由生效,需在路由配置中显式引用。
    • 全局过滤器:对所有路由生效,无需配置,常用于统一认证、日志收集等全局功能。
    • 过滤器执行顺序:请求转发前执行“pre”逻辑(如认证、参数校验),微服务响应后执行“post”逻辑(如响应改写、日志记录)。
  • 三、Spring Cloud Gateway环境搭建与基础路由配置

    3.1 环境准备

    3.1.1 技术栈选型
    • 开发语言:Java 11
    • 构建工具:Maven 3.6+
    • 框架:Spring Boot 2.7.x、Spring Cloud 2021.0.4、Spring Cloud Gateway 3.1.4
    • 依赖组件:Nacos Discovery(服务发现)、Sentinel(限流熔断)、Spring Security(认证)
    • 开发工具:IntelliJ IDEA、Postman(接口测试)
    3.1.2 项目初始化

    ① 创建Maven项目,命名为gateway-service,引入核心依赖:

    <!– Spring Boot父依赖 –>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.15</version>
    <relativePath/>
    </parent>

    <!– Spring Cloud依赖管理 –>
    <dependencyManagement>
    <<dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>2021.0.4</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </</dependencies>
    </dependencyManagement>

    <<dependencies>
    <!– Spring Cloud Gateway核心依赖(注意:排除spring-boot-starter-web,避免冲突) –>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <exclusions>
    <exclusion>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </exclusion>
    </exclusions>
    </dependency>

    <!– Nacos服务发现(用于动态路由,从Nacos获取微服务地址) –>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2021.0.4.0</version>
    </dependency>

    <!– Lombok –>
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
    </dependency>

    <!– 日志依赖(SLF4J+Logback) –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    </</dependencies>

    ⚠️ 注意:Spring Cloud Gateway基于Netty实现,与Spring MVC的spring-boot-starter-web存在冲突,必须排除spring-boot-starter-web依赖,否则启动报错。

    ② 编写启动类:

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

    @SpringBootApplication
    @EnableDiscoveryClient // 开启服务发现(从Nacos获取微服务列表)
    public class GatewayApplication {
    public static void main(String[] args) {
    SpringApplication.run(GatewayApplication.class, args);
    }
    }

    ③ 编写基础配置文件application.yml:

    spring:
    application:
    name: gatewayservice # 网关服务名称(注册到Nacos)
    cloud:
    # Nacos服务发现配置
    nacos:
    discovery:
    server-addr: localhost:8848 # Nacos地址
    # Gateway配置
    gateway:
    # 启用服务发现路由(自动从Nacos获取微服务,生成路由规则)
    discovery:
    locator:
    enabled: true # 开启服务发现路由
    lower-case-service-id: true # 微服务名称转为小写(如USER-SERVICE→user-service)
    # 路由规则配置(静态路由,优先级高于服务发现路由)
    routes:
    # 路由1:用户服务路由
    id: userserviceroute # 路由唯一ID(自定义,建议与服务名一致)
    uri: lb://userservice # 目标微服务地址(lb表示负载均衡)
    predicates: # 断言(匹配条件)
    Path=/api/user/** # 路径断言:匹配以/api/user/开头的请求
    filters: # 过滤器(局部过滤器,仅对当前路由生效)
    RewritePath=/api/user/(?<segment>.*), /$\\{segment} # 路径重写:/api/user/1 → /1
    # 路由2:订单服务路由
    id: orderserviceroute
    uri: lb://orderservice
    predicates:
    Path=/api/order/**
    filters:
    RewritePath=/api/order/(?<segment>.*), /$\\{segment}

    server:
    port: 8080 # 网关端口(客户端统一访问8080端口)

    # 日志配置(打印请求详细日志,便于调试)
    logging:
    level:
    org.springframework.cloud.gateway: DEBUG
    org.springframework.web.reactive: INFO

    3.2 基础路由测试

    3.2.1 启动依赖服务

    ① 启动Nacos Server(确保user-service、order-service已注册到Nacos)。 ② 启动user-service(端口8081)、order-service(端口8082)。 ③ 启动gateway-service(端口8080)。

    3.2.2 测试路由转发

    使用Postman发送请求,验证路由转发功能:

  • 访问用户服务接口:

    • 请求地址:http://localhost:8080/api/user/1
    • 预期结果:网关将请求转发到user-service的/1接口,返回用户信息:

    {
    "id": 1,
    "username": "张三1",
    "phone": "13800138001",
    "address": "北京市朝阳区"
    }

  • 访问订单服务接口:

    • 请求地址:http://localhost:8080/api/order/1
    • 预期结果:网关将请求转发到order-service的/1接口,返回订单信息:

    {
    "orderId": 1695888888888,
    "userId": 1,
    "username": "张三1",
    "productName": "Java编程思想",
    "price": 89.0,
    "count": 1,
    "totalAmount": 89.0,
    "status": "已创建"
    }

  • 服务发现路由测试:

    • 由于开启了discovery.locator.enabled=true,网关会自动生成路由:http://localhost:8080/服务名/接口路径
    • 测试地址:http://localhost:8080/user-service/user/1(直接通过服务名访问),返回结果与步骤1一致。
  • 3.3 核心断言工厂实战

    💡 Spring Cloud Gateway内置了11种断言工厂(Predicate Factory),支持根据路径、请求头、请求参数、时间、Cookie等条件匹配路由。断言工厂的命名格式为XXXRoutePredicateFactory,配置时只需写前缀(如Path对应PathRoutePredicateFactory)。

    3.3.1 常用断言工厂示例

    修改application.yml中的路由断言配置,演示常用断言的使用:

    spring:
    cloud:
    gateway:
    routes:
    id: userserviceroute
    uri: lb://userservice
    predicates:
    # 1. 路径断言(必选,匹配/api/user/**或/user/**)
    Path=/api/user/**, /user/**
    # 2. 方法断言(仅允许GET请求)
    Method=GET
    # 3. 请求头断言(必须包含X-Request-Id请求头)
    Header=XRequestId, \\d+ # 正则表达式:X-Request-Id的值为数字
    # 4. 请求参数断言(可选参数token,值包含"auth")
    Query=token, auth.*
    # 5. 时间断言(请求时间在2024-01-01之后)
    After=20240101T00:00:00+08:00[Asia/Shanghai]
    filters:
    RewritePath=/api/user/(?<segment>.*), /$\\{segment}
    RewritePath=/user/(?<segment>.*), /$\\{segment}

    3.3.2 断言测试

    使用Postman发送请求,验证断言效果:

  • 符合所有断言的请求:

    • 请求地址:http://localhost:8080/api/user/1?token=auth123
    • 请求头:X-Request-Id: 123456
    • 请求方法:GET
    • 预期结果:路由匹配成功,返回用户信息。
  • 不符合断言的请求:

    • 缺少X-Request-Id请求头 → 路由不匹配,返回404。
    • 请求方法为POST → 路由不匹配,返回404。
    • token参数值为test123(不包含auth) → 路由不匹配,返回404。
  • 3.3.3 自定义断言工厂(可选)

    若内置断言无法满足需求,可自定义断言工厂。例如,实现一个“IP白名单”断言,仅允许指定IP访问:

    ① 编写自定义断言工厂:

    import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.stereotype.Component;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.server.ServerWebExchange;

    import javax.validation.constraints.NotEmpty;
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Predicate;

    // 断言工厂名称:IpWhiteListRoutePredicateFactory → 配置时使用IpWhiteList
    @Component
    public class IpWhiteListRoutePredicateFactory extends AbstractRoutePredicateFactory<IpWhiteListRoutePredicateFactory.Config> {

    // 配置类(接收yaml中的配置参数)
    @Validated
    public static class Config {
    @NotEmpty(message = "IP白名单不能为空")
    private List<String> allowedIps;

    public List<String> getAllowedIps() {
    return allowedIps;
    }

    public void setAllowedIps(List<String> allowedIps) {
    this.allowedIps = allowedIps;
    }
    }

    // 构造方法:指定配置类
    public IpWhiteListRoutePredicateFactory() {
    super(Config.class);
    }

    // 配置参数解析(将yaml中的allowedIps转为List<String>)
    @Override
    public List<String> shortcutFieldOrder() {
    return Arrays.asList("allowedIps");
    }

    // 断言逻辑:判断请求IP是否在白名单中
    @Override
    public GatewayPredicate apply(Config config) {
    return new GatewayPredicate() {
    @Override
    public boolean test(ServerWebExchange exchange) {
    // 获取客户端IP(实际开发中需处理反向代理场景,如Nginx转发)
    ServerHttpRequest request = exchange.getRequest();
    String clientIp = request.getRemoteAddress().getAddress().getHostAddress();
    // 判断IP是否在白名单中
    return config.getAllowedIps().contains(clientIp);
    }

    @Override
    public String toString() {
    return "IpWhiteList: " + config.getAllowedIps();
    }
    };
    }
    }

    ② 配置自定义断言:

    spring:
    cloud:
    gateway:
    routes:
    id: userserviceroute
    uri: lb://userservice
    predicates:
    Path=/api/user/**
    IpWhiteList=127.0.0.1, 192.168.1.100 # 仅允许本地和192.168.1.100访问
    filters:
    RewritePath=/api/user/(?<segment>.*), /$\\{segment}

    ③ 测试效果:

    • 本地IP(127.0.0.1)访问 → 正常返回。
    • 其他IP(如192.168.1.101)访问 → 路由不匹配,返回404。

    四、过滤器开发实战:全局功能与局部定制

    4.1 过滤器分类与执行顺序

    💡 Spring Cloud Gateway的过滤器分为局部过滤器和全局过滤器,核心区别在于作用范围和配置方式:

    过滤器类型作用范围配置方式典型场景
    局部过滤器 特定路由 在路由的filters节点中配置 路径重写、局部限流、特定路由的参数处理
    全局过滤器 所有路由 实现GlobalFilter接口,无需配置 统一认证、日志收集、跨域处理、全局限流
    4.1.1 过滤器执行顺序
    • 局部过滤器:按配置文件中filters的顺序执行。
    • 全局过滤器:按Ordered接口的getOrder()方法返回值排序(值越小,执行优先级越高)。
    • 整体顺序:所有过滤器按优先级排序,“pre”逻辑按顺序执行,“post”逻辑按逆序执行。

    4.2 局部过滤器实战:路径重写与参数处理

    局部过滤器仅对当前路由生效,Spring Cloud Gateway内置了多种局部过滤器(如路径重写、请求头添加、参数添加等),以下是常用场景实战:

    4.2.1 路径重写(RewritePath)

    已在基础配置中演示,核心作用是将客户端请求路径改写为微服务的实际路径。例如:

    • 客户端请求:/api/user/1 → 改写为 /1 → 转发到user-service的/1接口。
    • 配置格式:RewritePath=原始路径正则, 目标路径(使用$\\{segment}引用正则分组)。
    4.2.2 添加请求头(AddRequestHeader)

    为转发到微服务的请求添加固定请求头,例如添加X-Gateway-Version标识网关版本:

    spring:
    cloud:
    gateway:
    routes:
    id: userserviceroute
    uri: lb://userservice
    predicates:
    Path=/api/user/**
    filters:
    RewritePath=/api/user/(?<segment>.*), /$\\{segment}
    AddRequestHeader=XGatewayVersion, v1.0.0 # 添加请求头

    4.2.3 添加请求参数(AddRequestParameter)

    为请求添加固定参数,例如添加source=gateway标识请求来源:

    filters:
    AddRequestParameter=source, gateway # 所有转发到user-service的请求都会携带source=gateway参数

    4.2.4 自定义局部过滤器

    实现一个局部过滤器,为特定路由的响应添加X-Response-Time头,记录请求处理耗时:

    ① 编写自定义局部过滤器:

    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;

    import java.util.Arrays;
    import java.util.List;

    // 局部过滤器工厂名称:ResponseTimeGatewayFilterFactory → 配置时使用ResponseTime
    @Component
    public class ResponseTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<ResponseTimeGatewayFilterFactory.Config> {

    // 配置类(无参数,仅作为标识)
    public static class Config {
    }

    public ResponseTimeGatewayFilterFactory() {
    super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
    return Arrays.asList(); // 无配置参数,返回空列表
    }

    @Override
    public GatewayFilter apply(Config config) {
    return (exchange, chain) -> {
    // 1. pre逻辑:记录请求开始时间
    long startTime = System.currentTimeMillis();
    // 2. 转发请求到微服务
    return chain.filter(exchange).then(Mono.fromRunnable(() -> {
    // 3. post逻辑:计算耗时,添加响应头
    long duration = System.currentTimeMillis() startTime;
    exchange.getResponse().getHeaders().add("X-Response-Time", duration + "ms");
    }));
    };
    }
    }

    ② 配置局部过滤器:

    spring:
    cloud:
    gateway:
    routes:
    id: userserviceroute
    uri: lb://userservice
    predicates:
    Path=/api/user/**
    filters:
    RewritePath=/api/user/(?<segment>.*), /$\\{segment}
    ResponseTime # 引用自定义局部过滤器

    ③ 测试效果:

    • 访问http://localhost:8080/api/user/1,查看响应头,会新增X-Response-Time: 20ms(耗时根据实际情况变化)。

    4.3 全局过滤器实战:统一认证与日志收集

    全局过滤器对所有路由生效,是实现统一功能的核心手段。以下是两个最常用的全局过滤器实战:

    4.3.1 统一认证过滤器(JWT令牌校验)

    💡 微服务架构中,客户端登录成功后获取JWT令牌,后续所有请求都需在请求头中携带Authorization: Bearer {token},网关统一校验令牌有效性,无效则直接返回401,避免每个微服务重复开发认证逻辑。

    ① 引入JWT依赖:

    <!– JWT依赖 –>
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
    </dependency>
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
    </dependency>
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
    </dependency>

    ② 编写JWT工具类:

    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.security.Keys;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;

    import javax.crypto.SecretKey;
    import java.util.Date;

    @Component
    public class JwtUtil {

    // JWT密钥(实际开发中需配置在Nacos,避免硬编码)
    @Value("${jwt.secret:abcdefghijklmnopqrstuvwxyz1234567890}")
    private String secret;

    // 令牌过期时间(2小时)
    @Value("${jwt.expire:7200000}")
    private long expire;

    // 生成密钥(HMAC-SHA256算法要求密钥长度至少256位)
    private SecretKey getSecretKey() {
    return Keys.hmacShaKeyFor(secret.getBytes());
    }

    // 生成JWT令牌
    public String generateToken(Long userId, String username) {
    return Jwts.builder()
    .setSubject(userId.toString()) // 主题(存储用户ID)
    .claim("username", username) // 自定义声明(存储用户名)
    .setIssuedAt(new Date()) // 签发时间
    .setExpiration(new Date(System.currentTimeMillis() + expire)) // 过期时间
    .signWith(getSecretKey()) // 签名
    .compact();
    }

    // 校验令牌有效性
    public boolean validateToken(String token) {
    try {
    Jwts.parserBuilder()
    .setSigningKey(getSecretKey())
    .build()
    .parseClaimsJws(token);
    return true;
    } catch (Exception e) {
    // 令牌过期、签名错误等都会抛出异常
    return false;
    }
    }

    // 解析令牌中的声明
    public Claims parseClaims(String token) {
    return Jwts.parserBuilder()
    .setSigningKey(getSecretKey())
    .build()
    .parseClaimsJws(token)
    .getBody();
    }
    }

    ③ 编写统一认证全局过滤器:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;

    @Component
    public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Autowired
    private JwtUtil jwtUtil;

    // 不需要认证的接口(白名单)
    private static final String[] WHITE_LIST = {
    "/api/user/login", // 登录接口
    "/api/user/register", // 注册接口
    "/actuator/health" // 健康检查接口
    };

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
    ServerHttpRequest request = exchange.getRequest();
    ServerHttpResponse response = exchange.getResponse();

    // 1. 判断请求路径是否在白名单中,若在则直接放行
    String path = request.getPath().value();
    for (String whitePath : WHITE_LIST) {
    if (path.startsWith(whitePath)) {
    return chain.filter(exchange);
    }
    }

    // 2. 从请求头中获取Authorization令牌
    String authHeader = request.getHeaders().getFirst("Authorization");
    if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) {
    // 3. 无令牌或令牌格式错误,返回401
    response.setStatusCode(HttpStatus.UNAUTHORIZED);
    return response.setComplete();
    }

    // 4. 提取令牌(去掉"Bearer "前缀)
    String token = authHeader.substring(7);
    if (!jwtUtil.validateToken(token)) {
    // 5. 令牌无效,返回401
    response.setStatusCode(HttpStatus.UNAUTHORIZED);
    return response.setComplete();
    }

    // 6. 令牌有效,解析用户信息,添加到请求头(供微服务使用)
    try {
    String userId = jwtUtil.parseClaims(token).getSubject();
    String username = jwtUtil.parseClaims(token).get("username", String.class);
    // 向请求头添加用户信息(微服务可通过请求头获取,无需再次解析JWT)
    ServerHttpRequest newRequest = request.mutate()
    .header("X-User-Id", userId)
    .header("X-User-Name", username)
    .build();
    // 替换请求对象
    return chain.filter(exchange.mutate().request(newRequest).build());
    } catch (Exception e) {
    response.setStatusCode(HttpStatus.UNAUTHORIZED);
    return response.setComplete();
    }
    }

    // 过滤器优先级:值越小越先执行(认证过滤器需优先执行)
    @Override
    public int getOrder() {
    return 100;
    }
    }

    ④ 测试认证功能:

  • 获取JWT令牌(模拟登录接口,实际开发中由user-service实现):

    • 假设user-service提供登录接口/api/user/login,传入用户名密码,返回令牌:

    {
    "code": 200,
    "msg": "登录成功",
    "data": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" // JWT令牌
    }

  • 携带令牌访问受保护接口:

    • 请求地址:http://localhost:8080/api/user/1
    • 请求头:Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…
    • 预期结果:令牌有效,正常返回用户信息。
  • 不携带令牌或令牌无效:

    • 直接访问http://localhost:8080/api/user/1 → 返回401 Unauthorized。
  • 4.3.2 全局日志过滤器

    收集所有请求的日志(请求路径、方法、IP、耗时、响应状态等),便于问题排查与系统监控:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;

    import java.util.UUID;

    @Component
    public class LogGlobalFilter implements GlobalFilter, Ordered {

    private static final Logger logger = LoggerFactory.getLogger(LogGlobalFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
    // 1. 生成请求唯一ID(用于追踪请求链路)
    String requestId = UUID.randomUUID().toString();
    ServerHttpRequest request = exchange.getRequest();
    ServerHttpResponse response = exchange.getResponse();

    // 2. pre逻辑:记录请求信息
    long startTime = System.currentTimeMillis();
    String method = request.getMethodValue();
    String path = request.getPath().value();
    String clientIp = request.getRemoteAddress().getAddress().getHostAddress();
    logger.info("【请求开始】requestId: {}, 客户端IP: {}, 方法: {}, 路径: {}, 请求头: {}",
    requestId, clientIp, method, path, request.getHeaders());

    // 3. 转发请求,监听响应完成事件
    return chain.filter(exchange).then(Mono.fromRunnable(() -> {
    // 4. post逻辑:记录响应信息
    long duration = System.currentTimeMillis() startTime;
    int statusCode = response.getStatusCode().value();
    logger.info("【请求结束】requestId: {}, 响应状态: {}, 耗时: {}ms",
    requestId, statusCode, duration);
    }));
    }

    // 日志过滤器优先级低于认证过滤器(先认证,再记录日志)
    @Override
    public int getOrder() {
    return 90;
    }
    }

    启动网关后,访问任意接口,控制台会输出类似日志:

    【请求开始】requestId: 550e8400-e29b-41d4-a716-446655440000, 客户端IP: 127.0.0.1, 方法: GET, 路径: /api/user/1, 请求头: [Authorization:"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…", X-Request-Id:"123456"]
    【请求结束】requestId: 550e8400-e29b-41d4-a716-446655440000, 响应状态: 200, 耗时: 35ms

    4.4 跨域处理(CORS)

    💡 当客户端(如前端Vue项目)与网关不在同一域名时,会出现跨域请求问题。Spring Cloud Gateway支持全局配置CORS,无需每个微服务单独配置:

    spring:
    cloud:
    gateway:
    globalcors:
    cors-configurations:
    '[/**]': # 对所有路径生效
    allowed-origins: "*" # 允许所有来源(生产环境建议指定具体域名,如https://www.example.com)
    allowed-methods: "*" # 允许所有HTTP方法(GET、POST、PUT、DELETE等)
    allowed-headers: "*" # 允许所有请求头
    allow-credentials: true # 允许携带Cookie
    max-age: 3600 # 预检请求缓存时间(1小时)

    五、网关高级功能:动态路由、限流熔断与高可用

    5.1 动态路由(基于Nacos配置)

    💡 基础路由配置写在application.yml中,修改后需重启网关才能生效,无法满足生产环境的动态调整需求。基于Nacos实现动态路由,可通过Nacos控制台修改路由配置,网关实时感知并生效,无需重启。

    5.1.1 集成Nacos Config

    ① 引入Nacos Config依赖:

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2021.0.4.0</version>
    </dependency>

    ② 创建bootstrap.yml文件(加载Nacos配置):

    spring:
    application:
    name: gatewayservice
    cloud:
    nacos:
    config:
    server-addr: localhost:8848 # Nacos配置中心地址
    file-extension: yaml # 配置文件格式
    namespace: public # 命名空间
    group: DEFAULT_GROUP # 配置分组
    profiles:
    active: dev # 环境标识

    ③ 在Nacos控制台创建配置:

    • Data ID:gateway-service-dev.yaml(格式:服务名-环境名.文件扩展名)。
    • 配置格式:YAML。
    • 配置内容(路由规则):

    spring:
    cloud:
    gateway:
    routes:
    id: userserviceroute
    uri: lb://userservice
    predicates:
    Path=/api/user/**
    filters:
    RewritePath=/api/user/(?<segment>.*), /$\\{segment}
    id: orderserviceroute
    uri: lb://orderservice
    predicates:
    Path=/api/order/**
    filters:
    RewritePath=/api/order/(?<segment>.*), /$\\{segment}

    5.1.2 实现动态路由刷新

    Spring Cloud Gateway与Nacos Config整合后,默认支持配置动态刷新,但路由规则的刷新需要自定义逻辑(监听Nacos配置变更,重新加载路由):

    ① 编写动态路由配置类:

    import com.alibaba.nacos.api.NacosFactory;
    import com.alibaba.nacos.api.config.ConfigService;
    import com.alibaba.nacos.api.config.listener.Listener;
    import com.alibaba.nacos.api.exception.NacosException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
    import org.springframework.cloud.gateway.route.RouteDefinition;
    import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.context.ApplicationEventPublisherAware;
    import org.springframework.stereotype.Component;
    import reactor.core.publisher.Mono;

    import javax.annotation.PostConstruct;
    import java.util.List;
    import java.util.Properties;
    import java.util.concurrent.Executor;

    @Component
    public class DynamicRouteConfig implements ApplicationEventPublisherAware {

    @Value("${spring.cloud.nacos.config.server-addr}")
    private String serverAddr;

    @Value("${spring.cloud.nacos.config.namespace}")
    private String namespace;

    @Value("${spring.cloud.nacos.config.group}")
    private String group;

    @Value("${spring.application.name}")
    private String serviceName;

    @Value("${spring.profiles.active}")
    private String activeProfile;

    // 路由定义写入器(用于更新路由)
    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    // 事件发布器(用于发布路由刷新事件)
    private ApplicationEventPublisher applicationEventPublisher;

    // Nacos配置服务
    private ConfigService configService;

    // 配置Data ID
    private String dataId;

    @PostConstruct
    public void init() throws NacosException {
    // 初始化Data ID
    dataId = serviceName + "-" + activeProfile + ".yaml";
    // 初始化Nacos ConfigService
    Properties properties = new Properties();
    properties.put("serverAddr", serverAddr);
    properties.put("namespace", namespace);
    configService = NacosFactory.createConfigService(properties);
    // 加载初始路由配置
    loadRouteConfig();
    // 注册Nacos配置监听器(监听配置变更)
    configService.addListener(dataId, group, new Listener() {
    @Override
    public void receiveConfigInfo(String configInfo) {
    // 配置变更时,重新加载路由
    loadRouteConfig();
    }

    @Override
    public Executor getExecutor() {
    return null;
    }
    });
    }

    // 加载路由配置(简化版:实际开发中需解析YAML为RouteDefinition列表)
    private void loadRouteConfig() {
    try {
    // 从Nacos获取配置
    String config = configService.getConfig(dataId, group, 5000);
    if (org.springframework.util.StringUtils.hasText(config)) {
    // 解析YAML配置为RouteDefinition列表(此处省略解析逻辑,推荐使用SnakeYAML)
    // 假设解析后得到List<RouteDefinition> routeDefinitions
    List<RouteDefinition> routeDefinitions = parseYamlToRouteDefinitions(config);

    // 1. 删除所有现有路由
    routeDefinitionWriter.delete(Mono.just("user-service-route")).subscribe();
    routeDefinitionWriter.delete(Mono.just("order-service-route")).subscribe();

    // 2. 添加新路由
    for (RouteDefinition routeDefinition : routeDefinitions) {
    routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
    }

    // 3. 发布路由刷新事件
    applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
    System.out.println("动态路由更新成功!");
    }
    } catch (Exception e) {
    System.err.println("动态路由更新失败:" + e.getMessage());
    }
    }

    // 解析YAML配置为RouteDefinition列表(实际开发中需实现)
    private List<RouteDefinition> parseYamlToRouteDefinitions(String config) {
    // 此处使用Spring的YamlPropertiesFactoryBean解析YAML,转换为RouteDefinition
    // 简化示例,实际需根据配置结构编写解析逻辑
    return null;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    this.applicationEventPublisher = applicationEventPublisher;
    }
    }

    ⚠️ 注意:实际开发中,parseYamlToRouteDefinitions方法需使用SnakeYAML或Spring的YamlPropertiesFactoryBean解析Nacos中的YAML配置,将其转换为List<RouteDefinition>对象,确保路由规则正确加载。

    5.1.3 测试动态路由
  • 在Nacos控制台修改gateway-service-dev.yaml,新增一个商品服务路由:
  • id: productserviceroute
    uri: lb://productservice
    predicates:
    Path=/api/product/**
    filters:
    RewritePath=/api/product/(?<segment>.*), /$\\{segment}

  • 点击“发布”,网关控制台输出“动态路由更新成功!”。
  • 访问http://localhost:8080/api/product/1,网关会自动转发到product-service,无需重启网关。
  • 5.2 网关限流与熔断(整合Sentinel)

    💡 网关作为流量入口,需要对进入系统的流量进行限流,避免后端微服务被压垮;同时,当后端微服务故障时,网关需要实现熔断,直接返回降级响应,避免故障扩散。Spring Cloud Gateway与Sentinel整合,可快速实现限流与熔断功能。

    5.2.1 集成Sentinel依赖

    <!– Sentinel核心依赖 –>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2021.0.4.0</version>
    </dependency>

    <!– Sentinel与Gateway整合依赖 –>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
    <version>2021.0.4.0</version>
    </dependency>

    <!– Sentinel控制台通信依赖 –>
    <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
    <version>1.8.6</version>
    </dependency>

    5.2.2 配置Sentinel

    spring:
    cloud:
    sentinel:
    transport:
    dashboard: localhost:8080 # Sentinel控制台地址(注意:与网关端口冲突时需修改Sentinel端口)
    port: 8720 # 与Sentinel控制台通信的端口(默认8719,避免冲突)
    # 网关限流配置
    scg:
    fallback:
    mode: response # 降级模式:返回自定义响应
    response-status: 429 # 降级响应状态码
    response-body: '{"code":429,"msg":"当前请求过于频繁,请稍后再试!"}' # 降级响应体

    ⚠️ 注意:Sentinel控制台默认端口为8080,与网关端口冲突,需修改Sentinel启动命令:

    java -jar sentinel-dashboard-1.8.6.jar –server.port=8083

    修改后Sentinel控制台地址为http://localhost:8083,网关配置中的spring.cloud.sentinel.transport.dashboard需同步修改为localhost:8083。

    5.2.3 配置网关限流规则(Sentinel控制台)
  • 启动网关、Sentinel控制台,访问网关接口(如http://localhost:8080/api/user/1),触发Sentinel监控。
  • 访问Sentinel控制台→网关限流→新增网关流控规则:
    • 资源名称:/api/user/**(路由路径)。
    • 限流指标:QPS。
    • 单机阈值:5(每秒最多允许5个请求)。
    • 其他默认,点击“新增”。
  • 5.2.4 测试限流效果

    快速访问http://localhost:8080/api/user/1(每秒超过5次),网关返回自定义降级响应:

    {"code":429,"msg":"当前请求过于频繁,请稍后再试!"}

    5.2.5 配置网关熔断规则

    当后端微服务(如user-service)故障时,网关熔断,避免频繁调用失败:

  • 在Sentinel控制台→熔断规则→新增熔断规则:
    • 资源名称:lb://user-service(微服务名称)。
    • 熔断策略:慢调用比例。
    • 最大RT:1000ms(超过1秒视为慢调用)。
    • 慢调用比例阈值:0.5(慢调用占比超过50%)。
    • 熔断时长:10s。
    • 最小请求数:5。
  • 故意修改user-service的接口,添加2秒延迟:
  • @GetMapping("/user/{id}")
    public User getUserById(@PathVariable Long id) throws InterruptedException {
    Thread.sleep(2000); // 模拟慢调用
    // 其余逻辑不变
    }

  • 快速访问http://localhost:8080/api/user/1(超过5次),触发熔断,网关返回降级响应。
  • 5.3 网关高可用部署

    💡 网关作为微服务架构的入口,一旦单点故障,整个系统将无法对外提供服务。因此,生产环境必须部署多个网关实例,配合负载均衡器(如Nginx)实现高可用。

    5.3.1 高可用架构图

    客户端 → Nginx(负载均衡) → 网关实例1(8080) → 微服务集群
    → 网关实例2(8081) → 微服务集群
    → 网关实例3(8082) → 微服务集群

    5.3.2 部署步骤
  • 启动多个网关实例:

    • 实例1:java -jar gateway-service.jar –server.port=8080
    • 实例2:java -jar gateway-service.jar –server.port=8081
    • 实例3:java -jar gateway-service.jar –server.port=8082
    • 所有实例注册到Nacos,自动实现服务发现与负载均衡。
  • 配置Nginx负载均衡: 编辑Nginx配置文件nginx.conf:

  • http {
    # 网关集群配置
    upstream gateway_cluster {
    server 127.0.0.1:8080 weight=1; # 权重1
    server 127.0.0.1:8081 weight=1; # 权重1
    server 127.0.0.1:8082 weight=1; # 权重1
    }

    server {
    listen 80; # Nginx监听80端口(客户端统一访问80端口)
    server_name localhost;

    location / {
    # 转发请求到网关集群
    proxy_pass http://gateway_cluster;
    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 X-Forwarded-Proto $scheme;
    }
    }
    }

  • 测试高可用:
    • 客户端访问http://localhost/api/user/1(Nginx端口80),Nginx会将请求转发到任意一个网关实例。
    • 关闭其中一个网关实例(如8080),客户端请求仍能正常响应(Nginx自动转发到其他实例)。
  • 六、网关性能优化与故障排查

    6.1 性能优化方案

    6.1.1 基础优化
  • 使用异步非阻塞模型:Spring Cloud Gateway默认基于Netty异步非阻塞,无需额外配置,避免使用同步过滤器(如依赖Spring MVC的组件)。
  • 调整Netty参数:
  • spring:
    cloud:
    gateway:
    httpclient:
    pool:
    max-idle-time: 60000 # 连接池最大空闲时间(60秒)
    max-connections: 1000 # 每个路由的最大连接数
    response-timeout: 5000 # 响应超时时间(5秒)

  • 启用连接复用:开启HTTP/2协议,支持连接复用,提升并发性能:
  • server:
    http2:
    enabled: true # 启用HTTP/2

  • 减少不必要的过滤器:仅保留核心功能过滤器(如认证、日志),避免过滤器链过长导致性能损耗。
  • 6.1.2 缓存优化
    • 对高频访问的静态资源(如图片、文档)或查询接口,在网关层添加缓存(如使用Redis),减少对后端微服务的请求。
    • 示例:使用spring-cloud-gateway-cache组件实现网关缓存(需额外引入依赖)。
    6.1.3 限流优化
    • 限流规则优先使用Sentinel的本地限流,避免分布式限流带来的网络开销。
    • 对不同路由配置不同的限流阈值,根据微服务的处理能力动态调整。

    6.2 故障排查方法

    6.2.1 日志排查
    • 开启Gateway DEBUG日志,打印请求转发细节:

    logging:
    level:
    org.springframework.cloud.gateway: DEBUG
    org.springframework.http.server.reactive: DEBUG
    reactor.netty: DEBUG

    • 关键日志包括:路由匹配过程、过滤器执行顺序、请求转发地址、响应状态等。
    6.2.2 监控排查
    • 集成Spring Boot Actuator,暴露网关监控指标:

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    management:
    endpoints:
    web:
    exposure:
    include: gateway,health,info # 暴露gateway监控端点
    endpoint:
    gateway:
    enabled: true # 启用网关监控

    • 访问http://localhost:8080/actuator/gateway/routes,查看当前路由配置;访问http://localhost:8080/actuator/gateway/globalfilters,查看全局过滤器。
    6.2.3 常见问题与解决方案
    常见问题解决方案
    路由匹配失败(404) 1. 检查路由Path断言是否正确;2. 检查微服务是否已注册到Nacos;3. 开启discovery.locator.enabled=true,测试服务名路由
    跨域请求失败(CORS) 检查网关全局CORS配置,确保allowed-origins、allowed-methods配置正确
    认证失败(401) 1. 检查JWT令牌是否有效;2. 检查请求头Authorization格式是否正确(Bearer + 空格 + 令牌);3. 检查白名单配置是否包含当前路径
    限流熔断不生效 1. 检查Sentinel依赖是否完整;2. 确认已访问网关接口触发Sentinel监控;3. 检查Sentinel控制台规则配置是否正确
    网关响应缓慢 1. 排查后端微服务响应时间;2. 检查过滤器链是否过长;3. 调整Netty连接池参数

    七、本章总结

    ✅ 本章详细讲解了微服务网关的核心价值、Spring Cloud Gateway的核心架构与实战应用,从基础路由配置到高级功能(动态路由、统一认证、限流熔断、高可用部署),完整覆盖了网关的设计与落地流程。

    通过本章学习,读者应掌握:

  • 微服务网关的核心作用与Spring Cloud Gateway的三大核心组件(路由、断言、过滤器)。
  • 局部过滤器与全局过滤器的开发,实现路径重写、统一认证、日志收集等功能。
  • 基于Nacos的动态路由与基于Sentinel的限流熔断实现。
  • 网关的高可用部署、性能优化与故障排查方法。
  • 网关作为微服务架构的“入口”,是系统安全防护、流量控制的关键环节。下一章将讲解微服务监控与运维体系,包括Prometheus + Grafana监控指标收集、ELK日志分析、SkyWalking链路追踪等内容,帮助读者构建完整的微服务运维体系。

    赞(0)
    未经允许不得转载:171主机测评 » 微服务网关设计与实战:Spring Cloud Gateway全解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址