使用 Spring Cloud Gateway 进行 API 限流

工程 | Haytham Mohamed | 2021 年 4 月 5 日 | ...

一项重要的架构考量是保护 API 和服务端点免受有害影响,例如拒绝服务、级联故障或资源过度使用。限流是一种控制 API 或服务使用速率的技术。在分布式系统中,将配置和管理消费者与 API 交互的速率集中化是最佳选择。只有在定义速率内的请求才能到达 API。任何超出速率的请求都会引发 HTTP“请求过多”错误。

link to rate limit image

Spring Cloud Gateway (SCG) 是一款简单轻量的组件,但它是管理限制 API 使用速率的有效方法。在本博文中,我将说明如何通过配置方法轻松实现这一点。如下图所示,演示包含一个前端和后端服务,以及介于两者之间的 Spring Cloud Gateway 服务。

link to rate limit image

将 SCG 集成到架构中无需任何代码。您需要在一个普通的 Spring Boot 应用程序中包含 Spring Boot Cloud 依赖项 org.springframework.cloud:spring-cloud-starter-gateway,然后就可以使用相应的配置设置了。

SCG 从前端服务接收到的请求可以根据配置的路由定义路由到后端服务。路由定义配置指定网关如何将请求路由到后端端点。路由配置通常根据可以从 HTTP 请求中提取的信息(例如路径和标头)定义条件。

例如,下面的代码段列出了一个 YAML 片段,用于配置将请求路由到后端服务的条件。它显示当网关在路径中接收到“/backend”时,请求应该目标到后端服务。在配置中,路由被赋予一个标识符和后端服务的 URI。

spring:
  cloud:
    gateway:
      routes:
        - id: route1
          uri: https://127.0.0.1:8081
          predicates:
            - Path=/backend

**RequestRateLimiter** 是 SCG 提供的众多网关过滤器之一。该实现确定请求是否允许继续或已超出其限制。该实现允许您(可选)插入一个键来管理限制对不同服务的请求数量。虽然它可以自定义以实现如何解析键,但网关带有一个使用用户的 **Principal** 名称的键。需要一个安全的网关来解析用户的 principal 名称,但您可以选择实现 KeyResolver 接口以从 ServerWebExchange 解析不同的键。您可以通过使用 SPEL #{@customKeyResolver} 表达式在配置中指向自定义 KeyResolver bean(例如,名为 customKeyResolver)。以下列表显示了 **KeyResolver** 接口

public interface KeyResolver {
    Mono<String> resolve(ServerWebExchange exchange);
}

如果未解析任何键,网关将拒绝请求。要让网关接受未解析的键,您可以设置以下属性

spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key=false

您还可以指定网关在无法确定键时应报告的状态代码,方法是设置以下属性

spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code=

考虑一个蓝图架构,其中网关使用 Redis 控制限制 API 使用。提供的 Redis 实现使用令牌桶算法。要使用它,您需要在网关应用程序中包含 spring-boot-starter-data-redis Spring Boot 启动器依赖项。基本上,令牌桶算法使用余额令牌作为维护利用累积预算的一种手段。该算法假设令牌将以一定的速率添加到桶中,而对 API 的调用会消耗这些令牌。一个 API 调用可能会执行许多操作来组合响应以完成请求(想想基于 GraphQL 的 API)。在这种情况下,该算法有助于识别一个调用可能花费 API 超过一个令牌。

link to rate limit image

提供的 Redis 实现允许您定义用户在特定时间段内可以进行调用的请求速率。它还可以适应偶发需求,同时受定义的消耗速率约束。例如,配置可以定义每秒 500 个请求的补充速率,方法是设置 redis-rate-limiter.replenishRate=500 属性,以及每秒 1000 个请求的突发容量,方法是设置 redis-rate-limiter.burstCapacity=1000 属性。这样做将消耗限制在每秒 500 个请求。如果请求数量发生突增,则仅允许 1000 个请求。但是,由于 1000 个请求是 2 秒的配额,因此网关将在下一秒内不会路由请求。配置还允许您通过设置属性 redis-rate-limiter.requestedTokens 属性来定义请求将花费多少个令牌。通常,它设置为 1。

要使用具有请求限制功能的网关,需要使用 RequestRateLimiter 网关过滤器对其进行配置。配置可以指定参数来定义补充速率、突发容量以及请求花费的令牌数量。以下示例说明了如何使用这些参数配置网关

spring:
  cloud:
    gateway:
      routes:
        - id: route1
          uri: https://127.0.0.1:8081
          predicates:
            - Path=/backend
          filters:
          - name: RequestRateLimiter
            args:
              redis-rate-limiter.replenishRate: 500
              redis-rate-limiter.burstCapacity: 1000
              redis-rate-limiter.requestedTokens: 1

Spring Cloud Gateway 提供了定义您自己的自定义限流实现的灵活性。它提供了一个 RateLimiter 接口来实现和定义 bean。可以使用 SPEL 表达式配置限流 bean,就像自定义键解析器的情况一样。例如,您可以定义一个名为 customRateLimiter 的自定义限流 bean 和一个名为 customKeyResolver 的自定义键解析器,并像这样配置路由

@Bean
public KeyResolver customKeyResolver {
	return exchange -> ....  // returns a Mono of String
}
spring:
  cloud:
    gateway:
      routes:
        - id: route1
          uri: https://127.0.0.1:8081
          predicates:
            - Path=/backend
          filters:
          - name: RequestRateLimiter
            args:
              rate-limiter: "#{customRateLimiter}"
              key-resolver: "#{customKeyResolver}"

一个说明性示例可在 GitHub 中找到

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证来加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部