Spring Cloud Gateway限流實戰

程式設計師欣宸發表於2021-11-23

歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;

本篇概覽

  • 本文是《Spring Cloud Gateway實戰》系列的第八篇,經過前面的學習,我們們對過濾器已瞭解得差不多,今天來補全過濾器的最後一個版塊:限流(RequestRateLimiter )
  • 預設的限流器是基於redis實現的,限流演算法是大家熟悉的令牌桶(Token Bucket Algorithm),關於令牌捅的原理就不在此展開了,聰明的您看一眼下圖應該就懂了:裝令牌的桶容量有限,例如最多20個,令牌進入桶的速度恆定(注意,這裡是和漏桶演算法的區別),例如每秒10個,底部每個請求能拿到令牌才會被處理:

在這裡插入圖片描述

RequestRateLimiter基本套路

  • 使用RequestRateLimiter過濾器的步驟非常簡單:
  1. 準備可用的redis
  2. maven或者gradle中新增依賴<font color="blue">org.springframework.boot:spring-boot-starter-data-redis-reactive</font>
  3. 確定按照什麼維度限流,例如按照請求中的username引數限流,這是通過編寫KeyResolver介面的實現來完成的
  4. 配置application.yml檔案,新增過濾器
  • 以上就是使用RequestRateLimiter過濾器的套路了,簡單麼?接下來,我們們先編碼再驗證

原始碼下載

名稱連結備註
專案主頁https://github.com/zq2599/blo...該專案在GitHub上的主頁
git倉庫地址(https)https://github.com/zq2599/blo...該專案原始碼的倉庫地址,https協議
git倉庫地址(ssh)git@github.com:zq2599/blog_demos.git該專案原始碼的倉庫地址,ssh協議
  • 這個git專案中有多個資料夾,本篇的原始碼在<font color="blue">spring-cloud-tutorials</font>資料夾下,如下圖紅框所示:

在這裡插入圖片描述

  • <font color="blue">spring-cloud-tutorials</font>資料夾下有多個子工程,本篇的程式碼是<font color="red">gateway-requestratelimiter</font>,如下圖紅框所示:

在這裡插入圖片描述

準備工作

  • 為了更好的演示Gateway的效果,在服務提供者<font color="blue">provider-hello</font>的程式碼(Hello.java)中新增一個web介面,可以接受一個入參:
    @GetMapping("/userinfo")
    public String userInfo(@RequestParam("username") String username) {
        return Constants.HELLO_PREFIX + " " + username + ", " + dateStr();
    }
  • 後面的測試我們們就用上述介面;

編碼

  • 在父工程<font color="blue">spring-cloud-tutorials</font>之下新增子工程<font color="red">gateway-requestratelimiter</font>,其pom.xml內容如下,重點是<font color="blue">org.springframework.boot:spring-boot-starter-data-redis-reactive</font>:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-tutorials</artifactId>
        <groupId>com.bolingcavalry</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway-requestratelimiter</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.bolingcavalry</groupId>
            <artifactId>common</artifactId>
            <version>${project.version}</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
    </dependencies>
</project>
  • 配置檔案application.yml,請注意RequestRateLimiter的幾個引數,已經用中文新增了詳細的註釋:
server:
  #服務埠
  port: 8081
spring:
  application:
    name: circuitbreaker-gateway
  # redis配置
  redis:
    host: 192.168.50.43
    port: 6379

  cloud:
    gateway:
      routes:
        - id: path_route
          uri: http://127.0.0.1:8082
          predicates:
            - Path=/hello/**
          filters:
            - name: RequestRateLimiter
              args:
                  # 令牌入桶的速度為每秒100個,相當於QPS
                redis-rate-limiter.replenishRate: 100
                # 桶內能裝200個令牌,相當於峰值,要注意的是:第一秒從桶內能去200個,但是第二秒只能取到100個了,因為入桶速度是每秒100個
                redis-rate-limiter.burstCapacity: 200
                # 每個請求需要的令牌數
                redis-rate-limiter.requestedTokens: 1
  • 指定限流維度的程式碼CustomizeConfig.java,這裡是根據請求引數<font color="blue">username</font>的值來限流的,假設真實請求中一半請求的username的等於<font color="red">Tom</font>,另一半的username的等於<font color="red">Jerry</font>,按照application.yml的配置,Tom的請求QPS為10,Jerry的QPS也是10:
package com.bolingcavalry.gateway.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import java.util.Objects;

@Configuration
public class CustomizeConfig {
    @Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));
    }
}
  • 毫無營養的啟動類RequestRateLimiterApplication.java:
package com.bolingcavalry.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RequestRateLimiterApplication {
    public static void main(String[] args) {
        SpringApplication.run(RequestRateLimiterApplication.class,args);
    }
}
  • 程式碼寫完了,接下來開始驗證;

驗證(桶容量等於入桶速度)

  • 首先驗證的是桶容量等於入桶速度時的效果,請修改<font color="blue">gateway-requestratelimiter</font>應用的application.yml中檔案,使得redis-rate-limiter.replenishRate和redis-rate-limiter.burstCapacity的值都等於100,也就是說桶的大小等於100,每秒放入的令牌數也是100
  • 確保redis已經啟動,並且與application.yml中的配置保持一直
  • 啟動nacos(provider-hello依賴)
  • 啟動服務提供者<font color="blue">provider-hello</font>
  • 啟動<font color="blue">gateway-requestratelimiter</font>
  • 為了模擬web請求,我這裡使用了<font color="blue">Apache Benchmark</font>,windows版本的下載地址:
    https://www.apachelounge.com/...
  • 上述檔案下載解壓後即可使用,在控制檯進入<font color="blue">Apache24\bin</font>後執行以下命令,意思是向指定地址傳送10000個請求,併發數為2:
ab -n 10000  -c 2 http://localhost:8081/hello/userinfo?username=Tom
  • 控制檯輸出如下,可見不到八秒的時間,只成功了800個,證明限流符合預期:

在這裡插入圖片描述

驗證(桶容量大於入桶速度)

  • 接下來試試桶容量大於入桶速度時的限流效果,這對於我們控制峰值響應有很重要的參考價值
  • 請修改<font color="blue">gateway-requestratelimiter</font>應用的application.yml中檔案,redis-rate-limiter.replenishRate維持<font color="blue">100</font>不變,但是redis-rate-limiter.burstCapacity改成<font color="red">200</font>,也就是說每秒放入的令牌數還是100,但桶的容量翻倍了
  • 重啟應用<font color="blue">gateway-requestratelimiter</font>
  • 再次執行以下命令,意思是向指定地址傳送10000個請求,併發數為2:
ab -n 10000  -c 2 http://localhost:8081/hello/userinfo?username=Tom
  • 測試結果如下圖,可見符合預期,可以將桶內令牌全部用掉,以支撐峰值超過QPS的場景:

在這裡插入圖片描述

驗證(根據username的維度限流)

  • 接下來驗證限流的維度,究竟是不是按照請求引數username的值來限流的
  • 我們們開啟兩個命令列,同時傳送請求(動作要快),第一個的username等於<font color="blue">Tom</font>,第二個等於<font color="blue">Jerry</font>,理論上推測,如果都是8秒內完成,那麼每個命令都有900個請求能成功
  • 測試結果如下圖,可見符合預期,每個username用的是自己的令牌:

在這裡插入圖片描述

  • 至此,Spring Cloud Gateway限流實戰已經完成,如此簡單易用的限流方案,希望能給您的學習和使用帶來參考

你不孤單,欣宸原創一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 資料庫+中介軟體系列
  6. DevOps系列

歡迎關注公眾號:程式設計師欣宸

微信搜尋「程式設計師欣宸」,我是欣宸,期待與您一同暢遊Java世界...
https://github.com/zq2599/blog_demos

相關文章