Spring Cloud Gateway current limiting practice

Welcome to my GitHub

https://github.com/zq2599/blog_demos

Content: classification and summary of all original articles and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc;

Overview of this article

  • This article is the eighth in the Spring Cloud Gateway series. After the previous study, we have almost understood the filter. Today, let's complete the last section of the filter: RequestRateLimiter

  • The default current limiter is implemented based on redis, and the current limiting algorithm is a familiar token bucket( Token Bucket Algorithm ), the principle of token poke is not discussed here. You should understand it by looking at the following figure: the bucket capacity of tokens is limited, for example, up to 20, and the speed of tokens entering the bucket is constant (note that this is the difference from the leaky bucket algorithm), for example, 10 tokens per second, and each request at the bottom will be processed only if it can get a token:

RequestRateLimiter basic routine

  • The steps to use the RequestRateLimiter filter are very simple:
  1. Prepare available redis
  2. Add dependency org.springframework.boot: spring boot starter data redis reactive in maven or gradle
  3. Determine what dimension to limit the flow according to, for example, the username parameter in the request. This is accomplished by writing the implementation of the KeyResolver interface
  4. Configure the application.yml file and add a filter
  • The above is the routine of using RequestRateLimiter filter. Is it simple? Next, let's code first and then verify

Source download

  • The complete source code in this actual combat can be downloaded from GitHub. The address and link information are shown in the table below( https://github.com/zq2599/blog_demos):
name link remarks
Project Home https://github.com/zq2599/blog_demos The project is on the GitHub home page
git warehouse address (https) https://github.com/zq2599/blog_demos.git The warehouse address of the source code of the project, https protocol
git warehouse address (ssh) git@github.com:zq2599/blog_demos.git The project source code warehouse address, ssh protocol
  • There are multiple folders in this git project. The source code of this article is in the spring cloud tutorials folder, as shown in the red box below:

  • There are several sub projects under the spring cloud tutorials folder. The code of this chapter is gateway requester, as shown in the red box below:

preparation

  • In order to better demonstrate the effect of Gateway, a new web interface is added in the code of service provider provider hello (Hello.java), which can accept an input parameter:
    @GetMapping("/userinfo")
    public String userInfo(@RequestParam("username") String username) {
        return Constants.HELLO_PREFIX + " " + username + ", " + dateStr();
    }
  • We will use the above interface for later tests;

code

  • Under the parent project spring cloud tutorials, add a new sub project gateway requeststratelimiter. Its pom.xml content is as follows, focusing on org.springframework.boot: spring boot starter data redis reactive:
<?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>
  • For the configuration file application.yml, please note that several parameters of requestratelimit have been added with detailed comments in Chinese:
server:
  #Service port
  port: 8081
spring:
  application:
    name: circuitbreaker-gateway
  # redis configuration
  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:
              	# The speed of token entering the bucket is 100 per second, which is equivalent to QPS
                redis-rate-limiter.replenishRate: 100
                # The bucket can hold 200 tokens, which is equivalent to the peak value. Note that 200 tokens can be removed from the bucket in the first second, but only 100 tokens can be obtained in the second second second, because the speed of entering the bucket is 100 tokens per second
                redis-rate-limiter.burstCapacity: 200
                # Number of tokens required per request
                redis-rate-limiter.requestedTokens: 1
  • The code that specifies the current limiting dimension is CustomizeConfig.java. Here, the current is limited according to the value of the request parameter username. It is assumed that half of the requested username in the real request is equal to Tom and the other half is equal to Jerry. According to the configuration of application.yml, Tom's request QPS is 10 and Jerry's QPS is also 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"));
    }
}
  • Startup class RequestRateLimiterApplication.java without nutrition:
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);
    }
}
  • After the code is written, start verification;

Verification (barrel capacity equals barrel feeding speed)

  • First, verify the effect when the bucket capacity is equal to the bucket speed. Please modify the file in application.yml of gateway requester application so that the values of redis-rate-limiter.replenishRate and redis-rate-limiter.burstCapacity are equal to 100, that is, the bucket size is equal to 100 and the number of tokens put in per second is also 100

  • Ensure that redis is started and consistent with the configuration in application.yml

  • Start nacos (provider Hello dependency)

  • Start service provider provider hello

  • Start the gateway requestrequest delimiter

  • In order to simulate the web request, I use Apache Benchmark, the download address of windows version:
    https://www.apachelounge.com/download/VS16/binaries/httpd-2.4.48-win64-VS16.zip

  • After downloading and decompressing the above files, you can use them. After entering Apache24\bin on the console, execute the following command, which means sending 10000 requests to the specified address, and the number of concurrent requests is 2:

ab -n 10000  -c 2 http://localhost:8081/hello/userinfo?username=Tom
  • The console output is as follows. It can be seen that in less than eight seconds, only 800 are successful, which proves that the current limit meets the expectation:

Verification (barrel capacity is greater than barrel feeding speed)

  • Next, try the current limiting effect when the barrel capacity is greater than the barrel inlet speed, which has a very important reference value for us to control the peak response

  • Please modify the file in the application.yml of the gateway requeststratelimiter application. redis-rate-limiter.replenishRate remains unchanged at 100, but redis-rate-limiter.burstCapacity is changed to 200, that is, the number of tokens put in per second is still 100, but the capacity of the bucket has doubled

  • Restart the application gateway requestdelimiter

  • Execute the following command again, which means that 10000 requests are sent to the specified address, and the number of concurrent requests is 2:

ab -n 10000  -c 2 http://localhost:8081/hello/userinfo?username=Tom
  • The test results are shown in the figure below. It can be seen that it meets the expectations. All tokens in the bucket can be used up to support the scenario where the peak exceeds QPS:

Validation (limit flow according to username's dimension)

  • Next, verify whether the current limiting dimension is limited according to the value of the request parameter username

  • Let's open two command lines and send requests at the same time (the action should be fast). The first username is equal to Tom and the second is equal to Jerry. Theoretically, if they are completed in 8 seconds, 900 requests for each command can succeed

  • The test results are shown in the figure below. It can be seen that it meets the expectations. Each username uses its own token:

  • So far, the current limiting practice of Spring Cloud Gateway has been completed. Such a simple and easy-to-use current limiting scheme is expected to bring reference to your study and use

You're not alone. Xinchen's original accompanies you all the way

  1. Java series
  2. Spring collection
  3. Docker series
  4. kubernetes series
  5. Database + middleware series
  6. DevOps series

Welcome to the official account: programmer Xin Chen

Wechat search "programmer Xinchen", I'm Xinchen, looking forward to traveling with you in the Java World
https://github.com/zq2599/blog_demos

Posted on Mon, 22 Nov 2021 20:34:20 -0500 by rachel2004