SpringCloud Alibaba learning (12)-GateWay gateway current limit

Why current limit

After the project goes online, the processing efficiency and resource consumption of each server is not unlimited. When the request is too high within the specified time period, it will cause the service resources to be overwhelmed and cause problems such as avalanches.

Common current limiting algorithms

Technological Algorithm

When doing Rate Limiting/Throttling, in addition to simple control of concurrency, if you want to accurately control TPS, the simple way is to maintain a Counter within a unit time. If it is judged that the unit time has passed, reset the Counter to zero. .

Insert picture description here
But this algorithm has a very fatal problem:

This approach is considered not to deal with the boundary of unit time very well.

For example, in 前一秒的最后一秒里and 下一秒的第一秒triggers a 最大number of requests, which is 在两秒内happening 两倍的 TPS.

Insert picture description here
Secondly, the last 1s and the first 1s request to be full. In the specified time period, there is a waste of resources in other intervals.
Insert picture description here

Leaky bucket algorithm

The Leaky Bucket algorithm is very simple. Water (request) enters the leaky bucket first, and the leaky bucket flows out at a certain speed (the interface has a response rate). When the water flows in too fast, it will overflow directly (the access frequency exceeds the interface response). Rate), and then reject the request, it can be seen that the leaky bucket algorithm can forcibly limit the data transmission rate.
Insert picture description here
The bottom layer adopts queue processing:
Insert picture description here

But there are also some resource consumption problems ( 突发请求处理).

1. When a large number of requests reach the gateway. Due to the stable request processing rate, a large number of requests accumulate in the gateway, resulting in a waste of gateway resources and excessive pressure.
2. Too many requests accumulate, resulting in a large number of requests being discarded.
3. Waste the processing of microservices, such as setting a request per second, but the processing efficiency of general microservices can reach hundreds of thousands per second, which is a serious waste of resources.

Token bucket algorithm

The token bucket algorithm is an improvement of the leaky bucket algorithm.

The Token Bucket algorithm has the same effect as the Leaky Bucket algorithm but in the opposite direction, which is easier to understand.

As time goes by, the system will add tokens to the bucket at a constant 1/QPS time interval (if QPS=100, the interval is 10ms) (imagine that, contrary to leaking, there is a faucet constantly adding water), if the bucket is full No more will be added.

When a new request comes, they will each take a Token, and if there is no Token available, it will block or deny the service.
Insert picture description here
The outline of the process is as follows:

1. Assume that the capacity of the token bucket is 10.
2. Generate the token at a constant rate. After the token is generated, the size of the token bucket will be judged. If there is no capacity, the token will be discarded; otherwise, it will be stored in the bucket.
3. Normally, every time a request comes, a token will be obtained from the token bucket.
4. If the token is taken out and the token is still being generated at this time, then discard the subsequent request or put it in the queue for buffering.

Project construction test

Gateway RequestRateLimiter official document

Dependency introduction

<!--引入redis依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!--对象池 redis高版本中未使用jedis,采取的netty非阻塞型连接池-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

Configuration file writing

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址
    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates: # 断言
            - Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
          filters: # 网关过滤器
            # 限流过滤器
            - name: RequestRateLimiter
              args:
                # 令牌桶每秒填充平均速率,即行等价于允许用户每秒处理多少个请求平均数
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的容量,允许在一秒钟内完成的最大请求数
                redis-rate-limiter.burstCapacity: 2
                # 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
                key-resolver: "#{@pathKeyResolver}" # pathKeyResolver是自己配置项目的bean的名称
                
  redis:
    database: 0
    host: 192.168.99.100
    port: 10000
    password: linkpower
    timeout: 10000 #连接超时时间
    lettuce:
      pool:
        #连接池最大连接数(使用负值表示没有限制)
        max-active: 300
        #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1s
        #连接池中的最大空闲连接
        max-idle: 100
        #连接池中的最小空闲连接
        min-idle: 20

Request path current limit

package cn.linkpower.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Configuration
public class MyResolverConf {
    
    

    /**
     * 限流规则<br/>
     * pathKeyResolver 必须和application.yml文件中的 #{@pathKeyResolver} 保持一致
     * @return
     */
    @Bean
    public KeyResolver pathKeyResolver(){
    
    
//        return new KeyResolver() {
    
    
//            @Override
//            public Mono<String> resolve(ServerWebExchange exchange) {
    
    
//                return Mono.just(exchange.getRequest().getPath().toString());
//            }
//        }
        return exchange -> Mono.just(exchange.getRequest().getPath().toString());
    }
}

Request a test:

http://localhost:10000/product/getProduct/5
Insert picture description here
Insert picture description here

Insert picture description here

Parameter current limit

With respect to the above profile, only need to modify ymland java 配置类corresponding to the configuration, the configuration is as follows:

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址
    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates: # 断言
            - Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
          filters: # 网关过滤器
            # 限流过滤器
            - name: RequestRateLimiter
              args:
                # 令牌桶每秒填充平均速率,即行等价于允许用户每秒处理多少个请求平均数
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的容量,允许在一秒钟内完成的最大请求数
                redis-rate-limiter.burstCapacity: 2
                # 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
                #key-resolver: "#{@pathKeyResolver}" # pathKeyResolver是自己配置项目的bean的名称 ——请求路径限流
                key-resolver: "#{@paramKeyResolver}" # pathKeyResolver是自己配置项目的bean的名称 ——参数限流

  redis:
    database: 0
    host: 192.168.99.100
    port: 10000
    password: linkpower
    timeout: 10000 #连接超时时间
    lettuce:
      pool:
        #连接池最大连接数(使用负值表示没有限制)
        max-active: 300
        #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1s
        #连接池中的最大空闲连接
        max-idle: 100
        #连接池中的最小空闲连接
        min-idle: 20

#  redis:
#    database: 0
#    host: 192.168.99.100
#    port: 10000
#    password: linkpower
#    timeout: 10000 #连接超时时间
#    jedis: ## jedis配置
#      pool: ## 连接池配置
#        max-idle: 8 # 最大空闲连接数
#        max-active: 8 # 最大连接数
#        max-wait: 3000 # 最大阻塞等待时间
#        min-idle: 0 # 最小空闲连接数


package cn.linkpower.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Configuration
public class MyResolverConf {
    
    

    /**
     * 限流规则<br/>
     * pathKeyResolver 必须和application.yml文件中的 #{@pathKeyResolver} 保持一致
     * @return
     */
    //@Bean
   // @Primary //添加这个注解是为了防止多个限流规则注册bean报错
    public KeyResolver pathKeyResolver(){
    
    
//        return new KeyResolver() {
    
    
//            @Override
//            public Mono<String> resolve(ServerWebExchange exchange) {
    
    
//                return Mono.just(exchange.getRequest().getPath().toString());
//            }
//        }
        return exchange -> Mono.just(exchange.getRequest().getPath().toString());
    }

    /**
     * 参数限流
     * @return
     */
    @Bean
    public KeyResolver paramKeyResolver(){
    
    
//        return new KeyResolver() {
    
    
//            @Override
//            public Mono<String> resolve(ServerWebExchange exchange) {
    
    
//                return Mono.just(exchange.getRequest().getPath().toString());
//            }
//        }
        // 随便指明一个参数
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userid"));
    }
}

When requesting a test at this time, the parameter information must be carried, because the parameter flow limit is added to the api in yml:
Insert picture description here
if the parameter request is not added, it is as follows:

http://localhost:10000/product/getProduct/5Insert picture description here
Insert picture description here

Add parameters and define different parameter requests:

http://localhost:10000/product/getProduct/5?userid=1

Current limiting operations will also occur.
However, different token buckets are set for different parameter values.

IP current limit

The configuration of ip current limit is the same as above, only need to modify the corresponding yml and java configuration classes.

server:
  port: 10000 # gateway的port

spring:
  application:
    name: gateway-server # 应用服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册地址
    gateway:
      routes:
        - id: nacos-product # 微服务别名称
          uri: lb://nacos-product # lb://服务别名  根据服务名称从注册中心获取服务ip+port信息,lb:// 表示支持负载均衡
          predicates: # 断言
            - Path=/product/** #path规则,匹配对应的URL请求,将匹配到的请求追加至目标URI之后
          filters: # 网关过滤器
            # 限流过滤器
            - name: RequestRateLimiter
              args:
                # 令牌桶每秒填充平均速率,即行等价于允许用户每秒处理多少个请求平均数
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的容量,允许在一秒钟内完成的最大请求数
                redis-rate-limiter.burstCapacity: 2
                # 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
                #key-resolver: "#{@pathKeyResolver}" # pathKeyResolver是自己配置项目的bean的名称 ——请求路径限流
                #key-resolver: "#{@paramKeyResolver}" # pathKeyResolver是自己配置项目的bean的名称 ——参数限流
                key-resolver: "#{@ipKeyResolver}" # pathKeyResolver是自己配置项目的bean的名称 ——ip限流

  redis:
    database: 0
    host: 192.168.99.100
    port: 10000
    password: linkpower
    timeout: 10000 #连接超时时间
    lettuce:
      pool:
        #连接池最大连接数(使用负值表示没有限制)
        max-active: 300
        #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1s
        #连接池中的最大空闲连接
        max-idle: 100
        #连接池中的最小空闲连接
        min-idle: 20

#  redis:
#    database: 0
#    host: 192.168.99.100
#    port: 10000
#    password: linkpower
#    timeout: 10000 #连接超时时间
#    jedis: ## jedis配置
#      pool: ## 连接池配置
#        max-idle: 8 # 最大空闲连接数
#        max-active: 8 # 最大连接数
#        max-wait: 3000 # 最大阻塞等待时间
#        min-idle: 0 # 最小空闲连接数
package cn.linkpower.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Configuration
public class MyResolverConf {
    
    

    /**
     * 限流规则<br/>
     * pathKeyResolver 必须和application.yml文件中的 #{@pathKeyResolver} 保持一致
     * @return
     */
   // @Bean
   // @Primary //添加这个注解是为了防止多个限流规则注册bean报错
    public KeyResolver pathKeyResolver(){
    
    
//        return new KeyResolver() {
    
    
//            @Override
//            public Mono<String> resolve(ServerWebExchange exchange) {
    
    
//                return Mono.just(exchange.getRequest().getPath().toString());
//            }
//        }
        return exchange -> Mono.just(exchange.getRequest().getPath().toString());
    }

    /**
     * 参数限流
     * @return
     */
    //@Bean
    public KeyResolver paramKeyResolver(){
    
    
//        return new KeyResolver() {
    
    
//            @Override
//            public Mono<String> resolve(ServerWebExchange exchange) {
    
    
//                return Mono.just(exchange.getRequest().getPath().toString());
//            }
//        }
        // 随便指明一个参数
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userid"));
    }

    @Bean
    public KeyResolver ipKeyResolver(){
    
    
//        return new KeyResolver() {
    
    
//            @Override
//            public Mono<String> resolve(ServerWebExchange exchange) {
    
    
//                return Mono.just(exchange.getRequest().getPath().toString());
//            }
//        }
        // 随便指明一个参数
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }
}

Test code

github code address

Guess you like

Origin blog.csdn.net/qq_38322527/article/details/113654721