使用 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 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 個請求。如果出現請求突發,只允許 1000 個請求。然而,由於 1000 個請求是 2 秒的配額,閘道器在接下來的第二秒將不再路由請求。配置還允許你透過設定屬性 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 表示式進行配置。例如,你可以定義一個名為 customRateLimiter 的自定義限流器 bean 和一個名為 customKeyResolver 的自定義鍵解析器,然後像這樣配置路由

@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 社群所有即將舉行的活動。

檢視全部