使用redis在SpringCloud getway中進行速率限制

2020-12-19 愛上樹的程序猿

使用redis在SpringCloud getway中進行速率限制

1.依存關係

我們將針對較高流量下的速率限制測試示例應用程式。首先,我們需要包括一些依賴項。當然,需要Spring Cloud Gateway啟動器。為了使用Redis處理速率限制器,我們還需要向spring-boot-starter-data-redis-reactive啟動器添加依賴項。其他依賴關係用於測試目的。mockserver測試容器內提供的模塊。它負責模擬目標服務。反過來,該庫在測試期間mockserver-client-java用於與mockserver容器集成。最後一個庫junit-benchmarks用於基準測試方法並同時運行測試。

<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>

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.testcontainers</groupId>

<artifactId>mockserver</artifactId>

<version>1.12.3</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.mock-server</groupId>

<artifactId>mockserver-client-java</artifactId>

<version>3.10.8</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>com.carrotsearch</groupId>

<artifactId>junit-benchmarks</artifactId>

<version>0.7.2</version>

<scope>test</scope>

</dependency>

該示例應用程式基於Spring Boot構建,2.2.1.RELEASE並使用Spring Cloud Hoxton.RC2Release Train。

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.2.1.RELEASE</version>

</parent>

<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<java.version>11</java.version>

<spring-cloud.version>Hoxton.RC2</spring-cloud.version>

</properties>

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-dependencies</artifactId>

<version>${spring-cloud.version}</version>

<type>pom</type>

<scope>import</scope>

</dependency>

</dependencies>

</dependencyManagement>

2.實施

請求速率限制是通過使用稱為的Spring Cloud Gateway組件實現的GatewayFilter。該過濾器的每個實例都由一個特定的工廠構造。過濾器當然負責在發送下遊請求之前或之後修改請求和響應。當前,有30個可用的內置 網關過濾器工廠。

在GatewayFilter有一個可選的keyResolver參數和參數特定於速率限制器的實現(在這種情況下使用的Redis的實施方案)。

參數keyResolver是實現KeyResolver接口的bean 。它允許您應用不同的策略來導出限制請求的密鑰。參數keyResolver是一個實現KeyResolver接口。

它允許您應用不同的策略來導出限制請求的密鑰。遵循Spring Cloud Gateway文檔:的默認實現KeyResolverPrincipalNameKeyResolver它Principal從ServerWebExchange和調用檢索Principal.getName()。默認情況下,如果KeyResolver找不到密鑰,則請求將被拒絕。可以使用spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key(true或false)和spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code屬性來調整此行為。

由於我們已經討論了速率限制的一些理論方面,因此我們可以繼續進行實施。首先,讓我們定義主類和非常簡單的KeyResolverbean,它始終等於一個。

@SpringBootApplication

public class GatewayApplication {

public static void main(String[] args) {

SpringApplication.run(GatewayApplication.class, args);

}

@Bean

KeyResolver userKeyResolver() {

return exchange -> Mono.just("1");

}

}

假設我們具有以下配置,並且目標應用程式在埠上運行,8091我們可以執行一些測試調用。您可以設置兩個屬性以自定義過程。該redis-rate-limiter.replenishRate決定每秒用戶多少個請求被允許發送,沒有任何下降的請求。這是令牌桶被填充的速率。第二個屬性redis-rate-limiter.burstCapacity是允許用戶在一秒鐘內執行的最大請求數。這是令牌桶可以容納的令牌數。將此值設置為零將阻止所有請求。

server:

port: ${PORT:8085}

spring:

application:

name: gateway-service

redis:

host: 192.168.99.100

port: 6379

cloud:

gateway:

routes:

- id: account-service

uri: http://localhost:8091

predicates:

- Path=/account/**

filters:

- RewritePath=/account/(?.*), /$\{path}

- name: RequestRateLimiter

args:

redis-rate-limiter.replenishRate: 10

redis-rate-limiter.burstCapacity: 20

現在,如果您呼叫網關公開的端點,則會收到以下響應。它包括一些特定的標頭,其前綴為x-ratelimit。標頭x-ratelimit-burst-capacity指示burstCapacity值,x-ratelimit-replenish-rate指示replenishRate值,最重要,指示x-ratelimit-remaining您可能在下一秒內發送的請求數。

如果您超出了允許的請求數量,Spring Cloud Gateway將返回帶有code的響應HTTP 429 - Too Many Requests,並且不會處理傳入的請求。

3.測試

我們有一個Spring Boot測試,該測試使用了Testcontainers提供的兩個Docker容器: MockServer和Redis。因為暴露埠是動態生成的,所以我們需要@BeforeClass在運行測試之前在方法中設置網關屬性。在init方法內部,我們還用於MockServerClient在模擬伺服器容器上定義模擬服務。我們的測試方法在六個線程中同時運行,並重複了600次。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)

@RunWith(SpringRunner.class)

public class GatewayRateLimiterTest {

private static final Logger LOGGER = LoggerFactory.getLogger(GatewayRateLimiterTest.class);

@Rule

public TestRule benchmarkRun = new BenchmarkRule();

@ClassRule

public static MockServerContainer mockServer = new MockServerContainer();

@ClassRule

public static GenericContainer redis = new GenericContainer("redis:5.0.6").withExposedPorts(6379);

@Autowired

TestRestTemplate template;

@BeforeClass

public static void init() {

System.setProperty("spring.cloud.gateway.routes[0].id", "account-service");

System.setProperty("spring.cloud.gateway.routes[0].uri", "http://192.168.99.100:" + mockServer.getServerPort());

System.setProperty("spring.cloud.gateway.routes[0].predicates[0]", "Path=/account/**");

System.setProperty("spring.cloud.gateway.routes[0].filters[0]", "RewritePath=/account/(?<path>.*), /$\\{path}");

System.setProperty("spring.cloud.gateway.routes[0].filters[1].name", "RequestRateLimiter");

System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.redis-rate-limiter.replenishRate", "10");

System.setProperty("spring.cloud.gateway.routes[0].filters[1].args.redis-rate-limiter.burstCapacity", "20");

System.setProperty("spring.redis.host", "192.168.99.100");

System.setProperty("spring.redis.port", "" + redis.getMappedPort(6379));

new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort())

.when(HttpRequest.request()

.withPath("/1"))

.respond(response()

.withBody("{\"id\":1,\"number\":\"1234567890\"}")

.withHeader("Content-Type", "application/json"));

}

@Test

@BenchmarkOptions(warmupRounds = 0, concurrency = 6, benchmarkRounds = 600)

public void testAccountService() {

ResponseEntity<Account> r = template.exchange("/account/{id}", HttpMethod.GET, null, Account.class, 1);

LOGGER.info("Received: status->{}, payload->{}, remaining->{}", r.getStatusCodeValue(), r.getBody(), r.getHeaders().get("X-RateLimit-Remaining"));

}

}

讓我們看一下測試結果。啟動網關後,用戶可以在一秒鐘內發送最多20個請求。超過此值後,它將開始返回HTTP 429。

刪除某些傳入請求後,網關將在下一秒開始接受它們。但是這一次它只允許處理10個請求,這等於replenishRate參數值。

相關焦點

  • SpringCloud Gateway動態路由
    一個環境新版本,一個環境舊版本,通過負載均衡進行切換與回滾,目的是為了減少服務停止時間。滾動部署就是在升級過程中,並不一下子啟動所有新版本,是先啟動一臺新版本,再停止一臺老版本,然後再啟動一臺新版本,再停止一臺老版本,直到升級完成。基於 k8s 的升級方案默認就是滾動部署。
  • 8張圖帶你認識SpringCloud框架(附spring源碼,建議收藏)
    4、redis緩存池,這個用來做session共享,分布式系統session共享是一個大問題。同時呢,redis做二級緩存對降低整個服務的響應時間,並且減少資料庫的訪問次數是很有幫助的。當然redis cluster還是redis sentinel自己選擇。5、eurake註冊中心這個高可用集群,這裡有很多細節,比如多久刷新列表一次,多久監測心跳什麼的,都很重要。
  • Spring Cloud 和 Dubbo,到底用哪個好?
    springcloud的接口協議約定比較自由且鬆散,需要有強有力的行政措施來限制接口無序升級dubbo的註冊中心可以選擇zk,redis等多種,springcloud的註冊中心只能用eureka或者自研但如果我選,我會用 Spring Cloud。
  • Spring Cloud Alibaba基礎教程:Sentinel使用Apollo存儲規則
    Apollo是國內用戶非常多的配置中心,所以,今天我們繼續說說Spring Cloud Alibaba Sentinel中如何將流控規則存儲在Apollo中。使用Apollo存儲限流規則Sentinel自身就支持了多種不同的數據源來持久化規則配置,目前包括以下幾種方式:本文我們就來一起動手嘗試一下,如何使用Apollo來存儲限流規則。
  • springcloud(十一):服務網關Zuul高級篇
    時間過的很快,寫springcloud(十):服務網關zuul初級篇還在半年前,現在已經是2018年了,我們繼續探討Zuul更高級的使用方式。上篇文章主要介紹了Zuul網關使用模式,以及自動轉發機制,但其實Zuul還有更多的應用場景,比如:鑑權、流量轉發、請求統計等等,這些功能都可以使用Zuul來實現。
  • F版本SpringCloud 2—什麼是SpringCloud?SpringCloud版本選擇
    NetFlix 是美國的一個在線視頻網站,微服務業的翹楚,他是公認的大規模生產級微服務的傑出實踐者,NetFlix的開源組件已經在他大規模分布式微服務環境中經過多年的生產實戰驗證,因此spring cloud中很多組件都是基於NetFlix組件的封裝在上篇文章中我們講到微服務是一套技術的合集,這些技術裡面有服務調用技術
  • 可以秒殺全場的SpringCloud微服務電商實戰項目,文檔賊全
    很多程式設計師每項技術單獨拿出來有可能很厲害,例如:springcloud、springboot、redis、nginx、mysql、rabbitMq等,但是普遍缺乏將所有的這些技術整合到一起,從前端到後端,從開發到部署上線,從每個知識點到整體的設計。
  • 安全的Spring Cloud配置
    它為分布式系統中的外部化配置提供伺服器端(Spring Cloud Config Server)和客戶端(Spring Cloud Config Server)支持。在本文中,我重點介紹與該項目有關的安全性。如果您對一些基礎知識感興趣,請參閱我以前的文章,其中涉及使用Spring Cloud Config進行微服務配置。
  • Spring Cloud AliBaba 開發教程:使用Nacos 實現服務註冊與發現
    dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId>
  • Spring Cloud Greenwich SR5 發布
    Spring Cloud Commons為了支持來自配置客戶端中 YAML 的合併列表,此版本進行了更改,以便將每個PropertySource分別添加到環境中,而不是添加到組合中。</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId>
  • springcloud五大組件
    首先我們來看springcloud是什麼?它是微服務架構集大成者,基於springboot構建,可以將一系列優秀組件進行完美整合。對熟悉的程式設計師來說,上手不麻煩,對新手來說,就需要了解springcloud架構再去學習。
  • SpringCloud Stream實戰快速入門
    POM主要增加了 org.springframework.cloud:spring-cloud-starter-stream-rocketmq 依賴,老顧採用最新Spring Boot的2.1.8.RELEASE版本,SpringCloud的Greenwich.SR2
  • springcloud的五大組件
    在介紹springcloud五大組件之前,先帶大家了解一下springcloud是什麼。Spring Cloud是一種最常見的分布式系統模式,為開發者提供了一種簡單並且易於接受的編程模型,幫助開發人員構建有彈性的、可靠的、協調的程序軟體,輕鬆實現微服務項目的構建,springcloud由五大組件組成!
  • springcloud實踐二:gateway網關詳解
    網關配合註冊中心也可以很好的實現微服務的動態擴容,只需要在網關將請求路由轉發到註冊中心的微服務上即可,由註冊中心進行負載均衡處理。Spring Cloud Gateway旨在提供一種簡單而有效的方法來路由到api,並為它們提供額外功能,例如:安全性、監控和彈性控制。
  • 出神入化的SpringCloudAlibaba問世,必須愛了
    為什麼使用spring cloud alibaba很多人可能會問,有了spring cloud這個微服務的框架,為什麼又要使用spring cloud alibaba這個框架了?最重要的原因在於spring cloud中的幾乎所有的組件都使用Netflix公司的產品,然後在其基礎上做了一層封裝。
  • Spring Cloud Gray,微服務灰度中間件
    和 spring-cloud-tray-server,spring-cloud-gray-webui組成。spring-cloud-gray-client定義了一套灰度路由決策模型,灰度信息追蹤模型,以及和spring-cloud-gray-server的基本通信功能。
  • SpringCloud簡介與5大常用組件
    springcloudspringcloud是微服務架構的集大成者,將一系列優秀的組件進行了整合。基於springboot構建,對我們熟悉spring的程式設計師來說,上手比較容易。通過一些簡單的註解,我們就可以快速的在應用中配置一下常用模塊並構建龐大的分布式系統。SpringCloud的組件相當繁雜,擁有諸多子項目。重點關注Netflix圖片來源@王璐-Louise。我是畫不出來這麼美的圖的。。。
  • 從零搭建 Spring Cloud 服務(超詳細)
    --引入springcloud的euekea server依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server
  • Spring Cloud Alibaba 發布第二個版本,Spring 發來賀電
    spring.cloud.nacos.config.ext-config[1].data-id=ext-config-common02.properties   spring.cloud.nacos.config.ext-config[1].group=GLOBAL_GROUP    # 3、Data Id 既不在默認的組,也支持動態刷新    spring.cloud.nacos.config.ext-config
  • Spring Cloud微服務構建搭構之分布式服務的跟蹤
    HTTP收集在Spring Cloud Sleuth中對Zipkin的整合進行了自動化配置的封裝,所以我們可以很輕鬆的引入和使用它,下面我們來詳細介紹一下Sleuth與Zipkin的基礎整合過程。我們除了需要之前引入的 spring-cloud-starter-sleuth依賴之外,還需要引入zipkin對Spring Cloud Stream的擴展依賴 spring-cloud-sleuth-stream以及基於Spring Cloud Stream實現的消息中間件綁定器依賴,以使用RabbitMQ為例,我們可以加入如下依賴:<dependency