Skip to content

Gateway 简介与核心作用

Spring Cloud Gateway 是 Spring Cloud 生态系统中的API网关服务,基于 Spring 5、Spring Boot 2 和 Project Reactor 等技术构建。它旨在为微服务架构提供一种简单、有效且统一的API路由管理方式。

Gateway 核心定位

Gateway 作为整个微服务架构的流量入口和边界控制器,所有外部请求首先到达网关,再由网关路由到具体的微服务。

Gateway 的核心作用

作用类别具体功能价值体现
路由转发动态路由、路径重写、负载均衡请求的智能分发和转发
安全防护认证授权、IP黑白名单、防重放攻击统一的安全屏障
流量治理限流熔断、降级策略、流量染色系统稳定性的保障
监控观测链路追踪、日志收集、指标监控全链路可观测性
业务赋能参数校验、协议转换、响应缓存业务逻辑的前置处理

基础路由配置

yaml
server:
  port: 8080 # 网关端口

spring:
  application:
    name: api-gateway # 网关服务名
  cloud:
    # 配置Nacos注册中心(以Nacos为例)
    nacos:
      discovery:
        server-addr: localhost:8848 # Nacos服务器地址
    # 配置网关
    gateway:
      # 开启从注册中心动态创建路由的功能
      discovery:
        locator:
          enabled: true # 启用动态路由
          lowerCaseServiceId: true # 使用小写服务名,可选,默认为false(大写服务名)。开启后,可通过 http://网关地址:端口/微服务名(小写)/** 格式访问
      # 配置静态路由
      routes:
        # 路由1:订单服务
        - id: order-service-route
          uri: lb://order-service
          predicates:
            - Path=/order/**
            - Method=GET,POST # 限制请求方法
        # 路由2:支付服务
        - id: payment-service-route
          uri: lb://payment-service
          predicates:
            - Path=/pay/**

# 暴露监控端点(可选,用于查看路由信息)
management:
  endpoints:
    web:
      exposure:
        include: gateway,health,info # 暴露gateway端点可查看路由信息

断言(Predicate)的使用

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: complex-route
          uri: lb://target-service
          predicates:
            # 路径匹配
            - Path=/api/v1/**
            # 时间窗口
            - After=2023-01-20T08:00:00.000+08:00
            - Before=2023-12-31T23:59:59.000+08:00
            # 请求方法
            - Method=GET,POST
            # 请求头匹配
            - Header=X-Request-Id, \\d+
            # Cookie匹配
            - Cookie=token, .+
            # 查询参数
            - Query=version, v\\d+
            # 客户端IP
            - RemoteAddr=192.168.1.1/24
            # 权重路由
            - Weight=group-a, 80

基于Java DSL的动态路由

java
@Configuration
public class GatewayConfig {
    
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("product_route", r -> r
                .path("/api/products/**") // 匹配 /api/products 路径
                .filters(f -> f // 添加过滤器
                    .stripPrefix(1)
                    .addRequestHeader("X-Request-From", "gateway")
                    .circuitBreaker(config -> config
                        .setName("productCircuitBreaker")
                        .setFallbackUri("forward:/fallback/product")
                    )
                )
                .uri("lb://product-service") // 路由到 product-service 服务
            )
            .route("auth_route", r -> r
                .path("/auth/**") // 匹配 /auth 路径
                .uri("lb://auth-service") // 路由到 auth-service 服务
            )
            .build();
    }
}

Gateway 的内置 Filter

内置过滤器配置即可使用:

yaml
filters:
  # 1. 请求头处理
  - AddRequestHeader=X-Gateway-Route, user-service
  - AddRequestParameter=source, gateway
  - RemoveRequestHeader=User-Agent
  
  # 2. 路径处理
  - StripPrefix=2
  - PrefixPath=/api/v1
  
  # 3. 响应处理
  - AddResponseHeader=X-Response-Time, $(currentTimeMillis)
  - DedupeResponseHeader=Access-Control-Allow-Origin
  
  # 4. 重试机制
  - name: Retry
    args:
      retries: 3
      methods: GET,POST
      exceptions: java.io.IOException,java.util.concurrent.TimeoutException
  
  # 5. 限流保护
  - name: RequestRateLimiter
    args:
      redis-rate-limiter.replenishRate: 10    # 每秒令牌数
      redis-rate-limiter.burstCapacity: 20    # 令牌桶容量
      key-resolver: "#{@ipKeyResolver}"       # 限流键解析器

自定义全局过滤器GlobalFilter

java
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        // 判断请求路径,决定是否执行过滤逻辑。示例:排除特定路径
        if (path.startsWith("/public/")) {
            return chain.filter(exchange); // 如果是公开路径,直接放行,不执行过滤逻辑
        }

        // 检查参数
        String uname = request.getQueryParams().getFirst( "uname");
        if(uname == null){
            log.info("参数用户名为nu11,非法用户");
            exchange.getResponse().setstatusCode(Httpstatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        // 验证通过,继续执行
        return chain.filter(exchange);
    }
    
    @Override
    public int getOrder() {
        return -1; // 高优先级,按顺序执行全部的过滤器
    }
}

自定义路由过滤器GatewayFilter

  1. 实现 GatewayFilter 接口,通常也会实现 Ordered 接口来控制过滤器的执行顺序(order 值越小,优先级越高)。
java
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class RequestTimeFilter implements GatewayFilter, Ordered {

    private static final String REQUEST_START_TIME = "requestStartTime";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1. Pre-filter 逻辑:记录请求开始时间
        exchange.getAttributes().put(REQUEST_START_TIME, System.currentTimeMillis());
        
        // 2. 继续执行过滤器链,并在链执行完毕后执行 Post-filter 逻辑
        return chain.filter(exchange).then(
            Mono.fromRunnable(() -> {
                Long startTime = exchange.getAttribute(REQUEST_START_TIME);
                if (startTime != null) {
                    long duration = System.currentTimeMillis() - startTime;
                    System.out.println("请求 " + exchange.getRequest().getURI().getRawPath() + " 处理耗时: " + duration + "ms");
                    // 通常这里会记录日志,而不是直接打印
                }
            })
        );
    }

    @Override
    public int getOrder() {
        // 设置过滤器执行顺序,这里设置为最低优先级
        return Ordered.LOWEST_PRECEDENCE;
    }
}
  1. 将此过滤器注册到路由:你需要通过 Java 配置(RouteLocator)将此过滤器绑定到特定路由。
java
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("api_route", r -> r.path("/api/**")
                .uri("lb://backend-service")
                // 将自定义过滤器添加到该路由
                .filters(new RequestTimeFilter()) 
                .id("api-with-timing"))
            .build();
    }
}

自定义路由过滤器(工厂+配置)

通过继承 AbstractGatewayFilterFactory 来创建自定义过滤器工厂,这样你就可以在 YAML 中像使用内置过滤器一样配置它。

  1. 以下是一个模拟的认证过滤器工厂示例:
java
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List;

@Component // 将其声明为Spring组件
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.Config> {

    // 构造函数
    public AuthGatewayFilterFactory() {
        super(Config.class);
    }

    // 指定配置文件中参数的顺序
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("enabled", "role");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // 如果未启用,直接放行
            if (!config.isEnabled()) {
                return chain.filter(exchange);
            }

            // 模拟认证逻辑:检查请求头中是否有"Authorization"
            String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
            if (!StringUtils.hasText(authHeader)) {
                // 若未找到认证头,返回401未授权
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }

            // 这里可以添加更复杂的认证和授权逻辑,例如解析JWT,校验config中的role等
            System.out.println("认证通过,进行角色校验: " + config.getRole());

            // 认证通过,继续执行过滤器链
            return chain.filter(exchange);
        };
    }

    // 静态内部配置类,用于接收配置文件中的参数
    public static class Config {
        private boolean enabled;
        private String role;

        // Getter 和 Setter 方法
        public boolean isEnabled() { return enabled; }
        public void setEnabled(boolean enabled) { this.enabled = enabled; }
        public String getRole() { return role; }
        public void setRole(String role) { this.role = role; }
    }
}
  1. 创建好过滤器工厂后,你就可以在配置文件中通过其名称(类名去掉"GatewayFilterFactory"后缀,如 Auth)来使用它。
yaml
spring:
  cloud:
    gateway:
      routes:
        - id: secure_api_route
          uri: lb://user-service
          predicates:
            - Path=/secure/**
          filters:
            # 使用自定义的认证过滤器,并传递参数
            - name: Auth # 使用自定义过滤器工厂
              args:
                enabled: true
                role: "ADMIN"

集成Sentinel实现熔断降级

在微服务架构中,使用 Spring Cloud Gateway 作为 API 网关集成 Sentinel 进行流量控制,是保障系统稳定性的常见做法。

防护位置选择可以参考如下:

  • 网关层:针对外部请求,进行粗粒度的全局流量控制和身份验证。
  • 微服务层:针对服务间调用,进行细粒度的熔断降级和业务逻辑保护。

如果需要集成 Sentinel,可以参考其他微服务一样的配置来配置 Gateway 服务即可使用。但请注意,spring-cloud-alibaba-sentinel-gateway 依赖非常关键,它为网关提供了专门的适配支持。缺少它可能导致流控规则不生效。

xml
<!-- Sentinel Starter -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- Sentinel Gateway Adapter (关键) -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

页脚:版权前显示的信息