一、为什么我们需要 API 网关?

在讲 Spring Cloud Gateway 之前,我们先搞清楚:没有网关的微服务架构,到底有哪些痛点?

1. 客户端复杂度高

如果没有网关,客户端需要直接调用各个微服务,需要维护所有服务的地址。一个系统有几十个微服务,客户端就要维护几十个地址,复杂度极高。

2. 代码冗余严重

每个微服务都要重复实现认证、授权、跨域、限流、日志、监控等横切关注点。这些功能和业务逻辑无关,但每个服务都要写一遍,代码冗余严重,维护成本极高。

3. 无法统一治理

没有统一的入口,无法对所有请求进行统一的流量控制、安全防护、日志审计。一旦出现问题,很难定位和排查。

4. 服务暴露风险高

所有微服务都直接暴露给外部,安全风险极高。攻击者可以直接攻击任何一个微服务,很容易造成系统瘫痪。

API 网关的核心价值

API 网关是微服务架构的统一入口,所有的外部请求都先经过网关,再由网关转发到对应的微服务。它主要解决以下问题:

  • 统一入口:客户端只需要和网关交互,不需要知道各个微服务的地址
  • 统一认证授权:所有请求都在网关层进行认证和授权,微服务不需要再实现这些功能
  • 统一流量治理:限流、熔断、降级、负载均衡都在网关层实现
  • 统一日志监控:所有请求的日志都在网关层统一收集和分析
  • 隐藏内部服务:微服务不直接暴露给外部,提高了系统的安全性
  • 协议转换:可以将 HTTP 协议转换为内部的 RPC 协议

二、Spring Cloud Gateway 是什么?

Spring Cloud Gateway 是 Spring Cloud 团队基于 Spring 5、Spring Boot 2 和 Project Reactor 开发的新一代 API 网关。它旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式。

为什么 Spring Cloud Gateway 取代了 Zuul?

Zuul 是 Netflix 开源的第一代网关,基于 Servlet 2.5,使用阻塞 IO,性能较差。而 Spring Cloud Gateway 基于非阻塞的 Reactor 框架,支持异步非阻塞,性能是 Zuul 的 2-3 倍。

两者的核心对比:

表格

特性 Zuul 1.x Spring Cloud Gateway
底层框架 Servlet Reactor + Netty
IO 模型 阻塞 IO 非阻塞 IO
性能 较差 优秀
支持异步 不支持 支持
功能丰富度 一般 丰富
社区活跃度

Spring Cloud Gateway 的核心特点

  • 基于 Spring Boot 2 和 Spring 5:无缝集成 Spring 生态
  • 非阻塞异步:基于 Reactor 框架,支持高并发
  • 强大的路由功能:支持多种路由断言和过滤器
  • 集成 Spring Cloud 生态:无缝集成 Nacos、Sentinel、Hystrix 等组件
  • 易于扩展:支持自定义断言和过滤器
  • 支持动态路由:可以动态修改路由规则,不需要重启服务

三、Spring Cloud Gateway 核心架构与概念

Spring Cloud Gateway 的核心架构非常简单,由三个核心概念组成:路由(Route)、断言(Predicate)、过滤器(Filter)

plaintext

┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  客户端请求  │───▶│  网关入口    │───▶│  断言匹配   │───▶│  过滤器链   │───▶│  目标微服务 │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘

1. 路由(Route)

路由是 Gateway 最基本的组件,它定义了请求的转发规则。一个路由由一个 ID、一个目标 URI、一组断言和一组过滤器组成。

当请求到达网关时,网关会根据断言来匹配路由,如果匹配成功,就会将请求转发到目标 URI,并经过过滤器链的处理。

2. 断言(Predicate)

断言是一个布尔表达式,用于判断请求是否符合某个条件。Spring Cloud Gateway 提供了多种内置的断言工厂,可以根据请求的路径、方法、头、参数、时间等进行匹配。

只有当所有断言都返回 true 时,请求才会匹配到对应的路由。

3. 过滤器(Filter)

过滤器用于对请求和响应进行修改和处理。Spring Cloud Gateway 提供了多种内置的过滤器工厂,可以实现添加请求头、添加响应头、重写路径、限流、熔断等功能。

过滤器分为两种:

  • 局部过滤器:只作用于某个特定的路由
  • 全局过滤器:作用于所有的路由

四、Spring Boot 整合 Spring Cloud Gateway

下面我就以 Spring Boot 2.7.x 和 Spring Cloud 2021.0.x 为例,教你如何快速整合 Spring Cloud Gateway。

第一步:引入依赖

xml

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<!-- 整合 Nacos 服务发现 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

第二步:配置基础路由

application.yml 中配置路由规则:

yaml

spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        # 用户服务路由
        - id: user-service-route
          uri: lb://user-service # lb:// 表示使用负载均衡
          predicates:
            - Path=/user/** # 路径断言:匹配所有 /user 开头的请求
          filters:
            - StripPrefix=1 # 去掉路径的第一个前缀,将 /user/xxx 转为 /xxx

        # 订单服务路由
        - id: order-service-route
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            - StripPrefix=1

这个配置的意思是:

  • 所有 /user/** 开头的请求,都会被转发到 user-service 服务
  • 所有 /order/** 开头的请求,都会被转发到 order-service 服务
  • StripPrefix=1 表示去掉路径的第一个前缀,比如 /user/get/1 会被转为 /get/1 再转发到 user-service

现在,你只需要启动网关服务,就可以通过网关访问各个微服务了。比如访问 http://localhost:8080/user/get/1,网关会自动转发到 user-service/get/1 接口。

五、核心功能详解

5.1 常用断言工厂

Spring Cloud Gateway 提供了非常丰富的断言工厂,下面是最常用的几个:

1. 路径断言(Path)

最常用的断言,根据请求路径进行匹配。

yaml

predicates:
  - Path=/user/**,/admin/** # 匹配多个路径
2. 方法断言(Method)

根据请求方法进行匹配。

yaml

predicates:
  - Method=GET,POST # 只匹配 GET 和 POST 请求
3. 请求头断言(Header)

根据请求头进行匹配。

yaml

predicates:
  - Header=token,^[a-zA-Z0-9]+$ # 匹配 token 请求头,值符合正则表达式
4. 请求参数断言(Query)

根据请求参数进行匹配。

yaml

predicates:
  - Query=name,zhangsan # 匹配 name 参数等于 zhangsan 的请求
5. 时间断言(Before/After/Between)

根据时间进行匹配。

yaml

predicates:
  - After=2024-01-01T00:00:00+08:00[Asia/Shanghai] # 匹配 2024 年 1 月 1 日之后的请求
6. 组合断言

多个断言可以组合使用,只有当所有断言都满足时,才会匹配路由。

yaml

predicates:
  - Path=/user/**
  - Method=GET
  - After=2024-01-01T00:00:00+08:00[Asia/Shanghai]

5.2 常用过滤器工厂

Spring Cloud Gateway 提供了几十种内置的过滤器工厂,下面是最常用的几个:

1. StripPrefix

去掉路径的前缀,最常用的过滤器。

yaml

filters:
  - StripPrefix=1 # 去掉第一个前缀
  - StripPrefix=2 # 去掉前两个前缀
2. AddRequestHeader

添加请求头。

yaml

filters:
  - AddRequestHeader=X-Request-Id,{requestId} # 添加请求头 X-Request-Id
3. AddResponseHeader

添加响应头。

yaml

filters:
  - AddResponseHeader=X-Response-Time,{responseTime} # 添加响应头 X-Response-Time
4. RewritePath

重写请求路径。

yaml

filters:
  - RewritePath=/api/(?<segment>.*),/$\{segment} # 将 /api/xxx 转为 /xxx
5. PrefixPath

添加路径前缀。

yaml

filters:
  - PrefixPath=/api # 给所有请求添加 /api 前缀
6. RequestRateLimiter

限流过滤器,基于 Redis 实现的令牌桶算法。

yaml

filters:
  - name: RequestRateLimiter
    args:
      redis-rate-limiter.replenishRate: 10 # 令牌桶每秒填充速率
      redis-rate-limiter.burstCapacity: 20 # 令牌桶最大容量
      key-resolver: "#{@userKeyResolver}" # 限流键解析器

5.3 全局过滤器

全局过滤器作用于所有的路由,不需要在每个路由上单独配置。Spring Cloud Gateway 内置了很多全局过滤器,比如负载均衡过滤器、路由转发过滤器等。

我们也可以自定义全局过滤器,实现统一的日志记录、认证授权等功能。

自定义全局日志过滤器示例

java

运行

@Component
@Slf4j
public class GlobalLogFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 记录请求开始时间
        long startTime = System.currentTimeMillis();
        
        // 获取请求信息
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().toString();
        String method = request.getMethod().name();
        String ip = request.getRemoteAddress().getAddress().getHostAddress();
        
        log.info("请求开始:{} {} {}", method, path, ip);
        
        // 执行过滤器链
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            // 记录响应信息
            long cost = System.currentTimeMillis() - startTime;
            ServerHttpResponse response = exchange.getResponse();
            int statusCode = response.getStatusCode().value();
            
            log.info("请求结束:{} {} {} 耗时:{}ms", method, path, statusCode, cost);
        }));
    }

    @Override
    public int getOrder() {
        // 过滤器执行顺序,值越小越先执行
        return -1;
    }
}

5.4 自定义过滤器

除了全局过滤器,我们还可以自定义局部过滤器,只作用于特定的路由。

自定义认证过滤器示例

java

运行

@Component
public class AuthFilter implements GatewayFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取 token
        String token = exchange.getRequest().getHeaders().getFirst("token");
        
        // 验证 token
        if (StringUtils.isEmpty(token) || !validateToken(token)) {
            // 认证失败,返回 401
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        
        // 认证成功,继续执行
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    private boolean validateToken(String token) {
        // 实际项目中这里应该调用认证服务验证 token
        return "valid_token".equals(token);
    }
}

然后在路由配置中使用这个过滤器:

yaml

routes:
  - id: user-service-route
    uri: lb://user-service
    predicates:
      - Path=/user/**
    filters:
      - StripPrefix=1
      - name: AuthFilter # 使用自定义认证过滤器

六、高级特性:网关限流与熔断降级

6.1 网关限流

限流是网关最重要的功能之一,它可以防止系统被突发流量打垮。Spring Cloud Gateway 内置了基于 Redis 的限流过滤器,使用令牌桶算法实现。

第一步:引入 Redis 依赖

xml

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

第二步:配置 Redis

yaml

spring:
  redis:
    host: localhost
    port: 6379
    password: 123456

第三步:定义限流键解析器

java

运行

@Configuration
public class RateLimiterConfig {
    @Bean
    public KeyResolver userKeyResolver() {
        // 根据用户 ID 限流
        return exchange -> Mono.just(
            Optional.ofNullable(exchange.getRequest().getHeaders().getFirst("userId"))
                .orElse("anonymous")
        );
    }

    @Bean
    public KeyResolver ipKeyResolver() {
        // 根据 IP 限流
        return exchange -> Mono.just(
            exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
        );
    }
}

第四步:在路由中配置限流

yaml

routes:
  - id: user-service-route
    uri: lb://user-service
    predicates:
      - Path=/user/**
    filters:
      - StripPrefix=1
      - name: RequestRateLimiter
        args:
          redis-rate-limiter.replenishRate: 10 # 每秒生成 10 个令牌
          redis-rate-limiter.burstCapacity: 20 # 令牌桶最大容量 20
          key-resolver: "#{@ipKeyResolver}" # 根据 IP 限流

6.2 熔断降级

当某个微服务出现故障时,网关可以直接熔断该服务的请求,返回降级结果,避免故障扩散到整个系统。Spring Cloud Gateway 可以很方便地整合 Sentinel 实现熔断降级。

第一步:引入 Sentinel 依赖

xml

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

第二步:配置 Sentinel

yaml

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080 # Sentinel 控制台地址
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: gateway-sentinel-rules
            groupId: DEFAULT_GROUP
            rule-type: flow

第三步:在 Sentinel 控制台配置熔断规则登录 Sentinel 控制台,就可以为各个路由配置流量控制、熔断降级规则了。

七、生产环境常见坑与最佳实践

坑 1:跨域问题

问题描述:前端调用网关接口时出现跨域错误。

解决方案:在网关层统一配置跨域:

yaml

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origin-patterns: "*"
            allowed-methods: "*"
            allowed-headers: "*"
            allow-credentials: true
            max-age: 3600

坑 2:路由匹配顺序问题

问题描述:多个路由的路径有重叠,导致请求匹配到了错误的路由。

解决方案:将更具体的路由放在前面,更通用的路由放在后面。Spring Cloud Gateway 会按照路由配置的顺序依次匹配。

坑 3:性能问题

问题描述:网关性能差,响应时间长。

解决方案

  • 避免在过滤器中执行耗时操作
  • 合理配置线程池参数
  • 开启网关缓存
  • 使用集群部署,提高并发能力

坑 4:网关单点故障

问题描述:网关是系统的统一入口,一旦网关挂了,整个系统都无法访问。

解决方案:部署多个网关实例,前面用 Nginx 做负载均衡,实现网关的高可用。

最佳实践

  1. 统一入口:所有外部请求都经过网关,微服务不直接暴露给外部
  2. 统一认证授权:所有认证授权都在网关层实现,微服务不需要关心
  3. 统一流量治理:限流、熔断、降级都在网关层实现
  4. 统一日志监控:所有请求的日志都在网关层统一收集
  5. 合理拆分路由:按照业务模块拆分路由,便于管理
  6. 避免过重的网关:网关只做横切关注点,不要在网关中实现业务逻辑
  7. 集群部署:生产环境必须部署多个网关实例,保证高可用
  8. 监控网关状态:监控网关的 QPS、响应时间、错误率等指标

总结

Spring Cloud Gateway 是微服务架构中不可或缺的核心组件,它是所有流量的必经之地,负责统一入口、统一认证、统一流量治理等所有横切关注点。

  • API 网关解决了微服务架构的诸多痛点,是微服务架构的必备组件
  • Spring Cloud Gateway 基于非阻塞的 Reactor 框架,性能优秀,是新一代网关的首选
  • 核心概念包括路由、断言和过滤器,掌握这三个概念就掌握了 Gateway 的核心
  • 内置了丰富的断言和过滤器工厂,可以满足绝大多数业务需求
  • 支持自定义全局过滤器和局部过滤器,易于扩展
  • 可以很方便地实现限流、熔断降级等高级功能
  • 生产环境要注意跨域、路由顺序、性能和高可用等问题
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐