Skip to content
第 222 / 250 章架构⏱ 12 分钟阅读

第 222 章:Spring Cloud Gateway 网关

学习目标

  • 理解 API 网关职责
  • 掌握 Gateway 路由配置
  • 学会自定义过滤器
  • 实现限流、熔断、鉴权

一、API 网关定位

1.1 网关职责

职责说明
统一入口对外暴露一个 IP / 域名
路由根据规则转发到对应服务
鉴权JWT / OAuth2 校验
限流QPS / IP 维度
熔断隔离下游故障
日志统一请求日志
协议转换HTTP → gRPC 等
灰度基于流量分发

1.2 网关对比

网关语言性能特性
Spring Cloud GatewayJava响应式、易扩展
Zuul 1.xJavaServlet,阻塞
KongLua/Nginx插件丰富
APISIXLua/Nginx云原生
EnvoyC++极高Service Mesh 标配
TraefikGo自动证书

二、Spring Cloud Gateway 基础

2.1 依赖

xml
<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>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2.2 路由配置

application.yml:

yaml
server:
  port: 9000

spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true                # 自动根据服务名路由
      routes:
        - id: order-service
          uri: lb://order-service    # lb = 负载均衡
          predicates:
            - Path=/api/orders/**
          filters:
            - StripPrefix=1          # 去掉 /api 前缀

        - id: payment-service
          uri: lb://payment-service
          predicates:
            - Path=/api/payments/**
            - Method=POST
          filters:
            - StripPrefix=1
            - AddRequestHeader=X-Source, gateway

        - id: api-prefix
          uri: lb://default-service
          predicates:
            - Path=/api/**

2.3 三大要素

要素作用
Route由 ID、URI、Predicate、Filter 组成
Predicate匹配条件,符合才路由
Filter在请求前后做处理(改写、鉴权、限流)

三、Predicate 断言

3.1 内置断言

yaml
predicates:
  # 路径匹配
  - Path=/api/orders/**
  # 方法
  - Method=GET,POST
  # Header
  - Header=X-Token, *
  # Cookie
  - Cookie=session, [a-z0-9]+
  # 查询参数
  - Query=name, .*
  # 时间
  - After=2026-01-01T00:00:00+08:00
  - Before=2027-01-01T00:00:00+08:00
  # Host
  - Host=**.example.com
  # 权重
  - Weight=group1, 8

3.2 自定义 Predicate

java
@Component
public class AuthTokenPredicate
    extends AbstractRoutePredicateFactory<AuthTokenPredicate.Config> {

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

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            String token = exchange.getRequest()
                .getHeaders().getFirst("Authorization");
            return token != null && token.startsWith("Bearer ");
        };
    }

    @Data
    public static class Config {
        private String tokenName;
    }
}

使用:

yaml
predicates:
  - name: AuthToken
    args:
      tokenName: Authorization

四、Filter 过滤器

4.1 内置 Filter

Filter说明
StripPrefix去掉前 N 段路径
PrefixPath添加前缀
AddRequestHeader添加请求头
RemoveRequestHeader删除请求头
AddResponseHeader添加响应头
Hystrix/Sentinel熔断降级
RequestRateLimiter限流
RedirectTo重定向
yaml
filters:
  - StripPrefix=1
  - AddRequestHeader=X-Source, gateway
  - AddResponseHeader=X-Response-Time, 100
  - RequestRateLimiter=10, 20, 100, user    # replenishRate=10, burstCapacity=20, 60s

4.2 GlobalFilter 全局过滤

java
@Component
public class LoggingGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest req = exchange.getRequest();
        log.info("[{}] {} from {}",
            req.getId(), req.getURI(), req.getRemoteAddress());

        long start = System.currentTimeMillis();
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            long cost = System.currentTimeMillis() - start;
            log.info("[{}] response cost={}ms", req.getId(), cost);
        }));
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

4.3 GatewayFilter 局部过滤

java
@Component
public class AuthGatewayFilterFactory
    extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.Config> {

    @Autowired
    private JwtUtil jwtUtil;

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

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String token = exchange.getRequest().getHeaders().getFirst(config.getTokenHeader());
            if (token == null || !jwtUtil.validate(token)) {
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            // 透传用户上下文
            String userId = jwtUtil.getUserId(token);
            exchange.getRequest().mutate()
                .header("X-User-Id", userId)
                .build();
            return chain.filter(exchange);
        };
    }

    @Data
    public static class Config {
        private String tokenHeader = "Authorization";
    }
}

使用:

yaml
filters:
  - name: Auth
    args:
      tokenHeader: Authorization

4.4 自定义 Filter 顺序

java
@Component
public class MyFilter implements GlobalFilter {
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 100;  // 越小越早执行
    }
}

五、限流

5.1 内置令牌桶

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                key-resolver: "#{@ipKeyResolver}"
java
@Bean
public KeyResolver ipKeyResolver() {
    return exchange -> Mono.just(
        exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
    );
}

5.2 Sentinel 网关限流

xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
yaml
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080

Dashboard 配置流控规则。

六、熔断降级

6.1 Sentinel 熔断

yaml
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080

spring.cloud.gateway.routes:
- id: order-service
  uri: lb://order-service
  filters:
    - name: SentinelGatewayFilter

6.2 自定义降级

java
@Component
public class FallbackHandler implements ErrorWebExceptionHandler {

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        DataBuffer buffer = exchange.getResponse()
            .bufferFactory()
            .wrap("服务暂时不可用".getBytes(StandardCharsets.UTF_8));
        exchange.getResponse().setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);
        return exchange.getResponse().writeWith(Mono.just(buffer));
    }
}

七、CORS 跨域

java
@Bean
public CorsWebFilter corsFilter() {
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedOriginPattern("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    config.setAllowCredentials(true);

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", config);
    return new CorsWebFilter(source);
}

八、灰度发布

8.1 基于 Header 路由

yaml
spring.cloud.gateway.routes:
- id: order-service-v1
  uri: lb://order-service
  predicates:
    - Path=/api/orders/**
    - Header=X-Gray, v1

- id: order-service-v2
  uri: lb://order-service-v2
  predicates:
    - Path=/api/orders/**
    - Header=X-Gray, v2

8.2 基于权重

yaml
spring.cloud.gateway.routes:
- id: order-v1
  uri: lb://order-service
  predicates:
    - Path=/api/orders/**
    - Weight=order-group, 90

- id: order-v2
  uri: lb://order-service-v2
  predicates:
    - Path=/api/orders/**
    - Weight=order-group, 10
java
public class GrayRoutePredicateFactory
    extends AbstractRoutePredicateFactory<GrayRoutePredicateFactory.Config> {

    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            HttpCookie cookie = exchange.getRequest()
                .getCookies()
                .getFirst(config.getCookieName());
            return cookie != null && config.getVersion().equals(cookie.getValue());
        };
    }
}

九、性能调优

9.1 Reactor Netty 参数

yaml
spring:
  cloud:
    gateway:
      httpclient:
        pool:
          type: elastic
          max-idle-time: 15s
        response-timeout: 30s
        connect-timeout: 5s
        max-initial-line-length: 4096

9.2 关闭多余 Filter

yaml
spring:
  cloud:
    gateway:
      default-filters: []   # 移除全局默认

9.3 Prometheus 监控

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
yaml
management:
  endpoints:
    web:
      exposure:
        include: "*"
  metrics:
    tags:
      application: api-gateway

查看:http://gateway:9000/actuator/prometheus

十、统一异常处理

java
@JsonDeserialize
public class ErrorResponse {
    private int code;
    private String message;
    private String path;
    private Instant timestamp = Instant.now();
}

@Component
public class GlobalErrorHandler implements ErrorWebExceptionHandler {

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        ErrorResponse err;
        if (ex instanceof ResponseStatusException rse) {
            err = new ErrorResponse(rse.getStatusCode().value(), rse.getReason(),
                exchange.getRequest().getPath().value());
            exchange.getResponse().setStatusCode(rse.getStatusCode());
        } else {
            err = new ErrorResponse(500, "Internal Server Error",
                exchange.getRequest().getPath().value());
            exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        }

        DataBuffer buffer = exchange.getResponse().bufferFactory()
            .wrap(objectMapper.writeValueAsBytes(err));
        return exchange.getResponse().writeWith(Mono.just(buffer));
    }
}

十一、生产配置示例

yaml
server:
  port: 9000

spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: nacos:8848
    gateway:
      discovery:
        locator:
          enabled: false     # 关闭自动,手动路由
      routes:
        # 订单服务
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 100
                redis-rate-limiter.burstCapacity: 200
                key-resolver: "#{@userKeyResolver}"

        # 库存服务
        - id: inventory-service
          uri: lb://inventory-service
          predicates:
            - Path=/api/inventory/**
          filters:
            - StripPrefix=1

        # 支付服务
        - id: payment-service
          uri: lb://payment-service
          predicates:
            - Path=/api/payments/**
            - Method=POST,GET
          filters:
            - StripPrefix=1

      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_UNIQUE

management:
  endpoints:
    web:
      exposure:
        include: health,info,gateway,prometheus

十二、本章小结

主题要点
路由ID + URI + Predicate
Predicate路径/方法/Header 匹配
Filter改写/鉴权/限流/熔断
限流RequestRateLimiter / Sentinel
熔断Sentinel Gateway Filter
灰度权重 / Header / Cookie
监控Prometheus 指标

动手练习

  1. 搭建 Gateway,实现路径路由 + 鉴权
  2. 自定义 GlobalFilter,添加请求 ID 和慢日志
  3. 配置 Sentinel 网关限流,验证令牌桶行为
  4. 实现基于 Header 的灰度发布(80%/20%)

推荐阅读


下一章:第 223 章:Nacos 配置与服务发现

本站基于 VitePress 构建 · 由 Codebook 团队维护