使用 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://: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 starter 依賴。基本上,令牌桶演算法使用平衡令牌作為維持累積利用預算的手段。該演算法假定令牌將以一定速率新增到桶中,而對 API 的呼叫則消耗這些令牌。一次 API 呼叫可能會執行許多操作來組合響應以完成請求(想想基於 GraphQL 的 API)。在這種情況下,該演算法有助於識別一次呼叫可能比一個令牌消耗更多的 API。

link to rate limit image

提供的 Redis 實現允許你定義使用者在特定時間段內可以進行呼叫的請求速率。它還使得在受限於定義的消耗速率的情況下適應零星需求成為可能。例如,配置可以透過設定 redis-rate-limiter.replenishRate=500 屬性來定義每秒 500 個請求的補充速率,並透過設定 redis-rate-limiter.burstCapacity=1000 屬性來定義每秒 1000 個請求的突發容量。這樣做將消耗限制為每秒 500 個請求。如果請求數量發生突發,只允許 1,000 個請求。但是,由於 1,000 個請求是 2 秒的配額,閘道器在接下來的 1 秒內將不會路由請求。該配置還允許你透過設定 redis-rate-limiter.requestedTokens 屬性來定義一個請求將花費多少令牌。通常,它設定為 1。

要使用具有請求限制功能的閘道器,需要使用 RequestRateLimiter 閘道器過濾器進行配置。配置可以指定引數來定義補充速率、突發容量以及請求所需的令牌數量。下面的示例演示瞭如何使用這些引數配置閘道器

spring:
  cloud:
    gateway:
      routes:
        - id: route1
          uri: https://: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。限速器 bean 可以使用 SPEL 表示式進行配置,就像自定義 key resolver 的情況一樣。例如,你可以定義一個名為 customRateLimiter 的自定義限速器 bean 和一個名為 customKeyResolver 的自定義 key resolver,並像這樣配置路由

@Bean
public KeyResolver customKeyResolver {
	return exchange -> ....  // returns a Mono of String
}
spring:
  cloud:
    gateway:
      routes:
        - id: route1
          uri: https://: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 社群所有即將舉行的活動。

檢視所有