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:
- Prepare available redis
- Add dependency in maven or gradle < font color = "blue" > org.springframework.boot: spring boot starter data redis reactive < / font >
- 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
- 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/blo...):
name | link | remarks |
---|---|---|
Project Home | https://github.com/zq2599/blo... | The project is on the GitHub home page |
git warehouse address (https) | https://github.com/zq2599/blo... | 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 < font color = "blue" > spring cloud tutorials < / font > folder, as shown in the red box below:
- There are several sub projects under the < font color = "blue" > spring cloud tutorials < / font > folder. The code of this chapter is < font color = "red" > gateway requester < / font >, as shown in the red box below:
preparation
- In order to better demonstrate the effect of Gateway, add a web interface in the code (Hello.java) of the service provider < font color = "blue" > provider Hello < / font >, 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 < font color = "blue" > spring cloud tutorials < / font >, a new sub project < font color = "red" > gateway requester < / font >, whose pom.xml content is as follows, focusing on < 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>
- 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 < font color = "blue" > username < / font >, assuming that half of the requested username in the real request is equal to < font color = "red" > Tom < / font >, and the other half of the username is equal to < font color = "red" >Jerry < / font >, 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 < font color = "blue" > gateway requester < / font > application so that the values of redis-rate-limiter.replenshrate 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 the service provider < font color = "blue" > provider Hello < / font >
- Start < font color = "blue" > gateway requester < / font >
- To simulate web requests, I use < font color = "blue" > Apache benchmark < / font >, the download address of windows version:
https://www.apachelounge.com/... - After downloading and decompressing the above files, you can use them. After entering < font color = "blue" > apache24 \ bin < / font > on the console, execute the following command, which means to send 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 application.yml of < font color = "blue" > gateway requestelimiter < / font > application. Redis rate limit.replenishrate remains < font color = "blue" > 100 < / font > unchanged, but redis rate limit.burstcapacity is changed to < font color = "red" > 200 < / font >, that is to say, the number of tokens put in each second is still 100, but the capacity of the bucket has doubled
- Restart the application < font color = "blue" > gateway requester < / font >
- 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 < font color = "blue" > Tom < / font >, and the second is equal to < font color = "blue" > Jerry < / font >. 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
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