Multiple ways of Spring cloud current limiting

  • 2021-09-12 01:01:08
  • OfStack

Directory 1. Practical current limiting based on Spring cloud Gateway
2. Based on Ali open source current limiting artifact: Sentinel

In the case of frequent network requests, the service is sometimes under great pressure, especially the kind of network attacks, illegal. This situation sometimes needs to be limited. For example, limit the request of the other party, which can be based on several reasons: request IP, user only 1 identity, request interface address, etc.

At present, there are many ways to limit current: Spring cloud has one function of limiting current in the gateway itself, which is based on redis. At the same time, Ali also opened an open source: current limiting artifact Sentinel. Today, we mainly focus on these two pieces to actual combat the current limiting mechanism of micro-services.

First of all, we will talk about the native current limiting function of Spring cloud, because current limiting can be used to limit the current of each service or to limit the current of gateway system 1.

1. Current limiting based on Spring cloud Gateway in actual combat

pom. xml introduces dependencies:


<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>

Its foundation is based on redis, so:


spring:
  application:
    name: gateway-service
  redis: #redis Related configuration 
    database: 8
    host: 10.12.15.5
    port: 6379
    password: 123456 # Set when there is a password 
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
    timeout: 10000ms

Next, bean with current limiting strategy needs to be injected:


@Primary
  @Bean(value = "ipKeyResolver")
  KeyResolver ipKeyResolver() {
      return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
      //return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
      //return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
  }

 
  @Bean(value = "apiKeyResolver")
  KeyResolver apiKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getPath().value());
  }

  
  @Bean(value = "userKeyResolver")
  KeyResolver userKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
  }

Three strategies, ipKeyResolver, apiKeyResolver and userKeyResolver, are introduced here, and one of them can be used by using the annotation @ Primary.

After bean is injected, it needs to be standby in the configuration:


spring:
  application:
    name: gateway-service
  redis: #redis Related configuration 
    database: 8
    host: 10.12.15.5
    port: 6379
    password: 123456 # Set when there is a password 
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
    timeout: 10000ms

The following is the main configuration of current limiting:


spring
  cloud:
    gateway:
      routes: # Routing configuration : Parameter is 1 A List
      - id: cas-server # Only 1 Identification 
        uri: lb://cas-server-service # Forwarded address , Write service name 
        order: -1
        predicates:
        - Path=/cas-server/** # Judge matching conditions , That is, the address has a /ribbon/** Request for , Will be forwarded to lb:cas-server-service
        filters:
        - StripPrefix=1 # Remove Path Prefix , Parameter is 1 Represents removal /ribbon

        - name: RequestRateLimiter # Based on redis Adj. Gateway Self-current limiting of 
          args:
            redis-rate-limiter.replenishRate: 1  #  How many requests per second are allowed for users 
            redis-rate-limiter.burstCapacity: 3  #  The capacity of the token bucket, which is allowed in the 1 Maximum number of requests completed in seconds 
            key-resolver: "#{@ipKeyResolver}" #SPEL The corresponding one taken by the expression bean

      - id: admin-web
        uri: lb://admin-web-service
        order: -1
        predicates:
        - Path=/admin-web/**
        filters:
        - StripPrefix=1

        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 1  #  How many requests per second are allowed for users 
            redis-rate-limiter.burstCapacity: 3  #  The capacity of the token bucket, which is allowed in the 1 Maximum number of requests completed in seconds 
            key-resolver: "#{@ipKeyResolver}" #SPEL The corresponding one taken by the expression bean

Here, RequestRateLimiter current limiting filter is added on the basis of the original route, including three parameters:


- name: RequestRateLimiter # Based on redis Adj. Gateway Self-current limiting of 
          args:
            redis-rate-limiter.replenishRate: 3  # How many requests per second are allowed for users 
            redis-rate-limiter.burstCapacity: 5  # The capacity of the token bucket, which is allowed in the 1 Maximum number of requests completed in seconds 
            key-resolver: "#{@ipKeyResolver}" #SPEL The corresponding one taken by the expression bean
Where replenishRate, its meaning means the number of requests allowed to be processed per second; burstCapacity represents the maximum number of requests allowed to be processed in 1 second; In key-resolver, the request IP is used to limit the current, and the corresponding bean is obtained by using SPEL expression

Write a small script and test it under 1 pressure:


for i in $(seq 1 30000); do echo $(expr $i \\* 3 + 1);curl -i -H "Accept: application/json" -H "Authorization:bearer b064d95b-af3f-4053-a980-377c63ab3413" -X GET http://10.10.15.5:5556/order-service/api/order/getUserInfo;done

for i in $(seq 1 30000); do echo $(expr $i \\* 3 + 1);curl -i -H "Accept: application/json" -H "Authorization:bearer b064d95b-af3f-4053-a980-377c63ab3413" -X GET http://10.10.15.5:5556/admin-web/api/user/getCurrentUser;done

The above two scripts test the two services and print the results:

{"message":{"status":200,"code":0,"message":"success"},"data":"{\"message\":{\"status\":200,\"code\":0,\"message\":\"get user success\"},\"data\":{\"id\":23,\"isAdmin\":1,\"userId\":\"fbb18810-e980-428c-932f-848f3b9e7c84\",\"userType\":\"super_admin\",\"username\":\"admin\",\"realName\":\"super_admin\",\"password\":\"$2a$10$89AqlYKlnsTpNmWcCMvgluRFQ/6MLK1k/nkBpz.Lw6Exh.WMQFH6W\",\"phone\":null,\"email\":null,\"createBy\":\"admin\",\"createTime\":1573119753172,\"updateBy\":\"admin\",\"updateTime\":1573119753172,\"loginTime\":null,\"expireTime\":null,\"remarks\":\"super_admin\",\"delFlag\":0,\"loginType\":null}}"}ex

After multiple requests in the same 1 second with the test tool Jmeter:


HTTP/1.1 429 Too Many Requests
X-RateLimit-Remaining: 0
X-RateLimit-Burst-Capacity: 3
X-RateLimit-Replenish-Rate: 1
content-length: 0

expr: syntax error

HTTP/1.1 429 Too Many Requests
X-RateLimit-Remaining: 0
X-RateLimit-Burst-Capacity: 3
X-RateLimit-Replenish-Rate: 1
content-length: 0

expr: syntax error

As you can see from the above, after execution, the call fails and the status changes to 429 (Too Many Requests).

2. Based on Ali open source current limiting artifact: Sentinel

First introduce dependencies:


<!-- Based on   Ali's sentinel Act as current limiting  -->
    <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

Configuration in the configuration file application. yaml requires two new configurations:


spring:
  application:
    name: admin-web
  cloud:
    kubernetes:
      discovery:
        all-namespaces: true
    sentinel:
      eager: true # Cancel Sentinel Lazy loading of console 
      transport:
        dashboard: 10.12.15.2:8080 #sentinel Adj. Dashboard Address 
        port: 8719 # Yes sentinel Application and console communication port 
        heartbeat-interval-ms: 500 # Heartbeat time 
      scg:
        fallback: #scg.fallback For sentinel Response configuration after current limiting 
          mode: response
          response-status: 455
          response-body:  Current has been limited 

Among them, one service is configured: spring. cloud. sentinel. transport. dashboard, and the Dashboard address of sentinel is configured. At the same time, the port configuration of spring. cloud. sentinel. transport. port will start an Http Server on the corresponding machine of the application, and the Server will interact with the Sentinel console.

Sentinel provides current limiting embedding points for all HTTP services by default. After the above configuration is completed, all embedding points are automatically completed, and only the current limiting rules need to be controlled and configured.
Here, we talk about adding current limiting embedding points to specified interface functions through annotations, writing an RestController, and adding annotations to interface functions


spring:
  application:
    name: gateway-service
  redis: #redis Related configuration 
    database: 8
    host: 10.12.15.5
    port: 6379
    password: 123456 # Set when there is a password 
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
    timeout: 10000ms
0

The above code is partially completed. Next, install SentinelDashBoard, Sentinel DashBoard Download address: github. com/alibaba/Sentinel/releases.

After the download is complete, the command starts:


spring:
  application:
    name: gateway-service
  redis: #redis Related configuration 
    database: 8
    host: 10.12.15.5
    port: 6379
    password: 123456 # Set when there is a password 
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
    timeout: 10000ms
1

The default startup port is 8080. When you visit IP: 8080, you can display the login interface of Sentinel. The user name and password are sentinel. After logging in to Dashboard successfully, you can access the interface "/getToken" several times, and you can see the corresponding data in Dashboard, which is not shown here. Next, you can set the current limiting function of the interface, click on the "+ Flow Control" button to open the setting interface, and set the threshold type to qps and the stand-alone threshold to 5.

The browser repeatedly requests http://10.10.15.5: 5556/admin-web/api/user/getToken If the threshold is exceeded, the following interface message will appear:
Blocked by Sentinel (flow limiting)

At this point, you can see that Sentinel current limiting is working. You can add spring. cloud. sentinel. scg. fallback as the response configuration after sentinel current limiting, and you can also customize the abnormal information of current limiting:


spring:
  application:
    name: gateway-service
  redis: #redis Related configuration 
    database: 8
    host: 10.12.15.5
    port: 6379
    password: 123456 # Set when there is a password 
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
    timeout: 10000ms
2

Here, the annotation @ SentinelResource contains the following attributes:

value: Resource name, required; entryType: Entry type, optional (default is EntryType. OUT); blockHandler: The name, parameter type, and return value of the corresponding exception handling method in blockHandlerClass must be the same as the original method 1; blockHandlerClass: Custom Current Limiting Logic Handler Class

The Sentinel current limiting logic is processed, but each time the service is restarted, the previously configured current limiting rules are emptied. Because it is a rule object in memory. Therefore, we will use ReadableDataSource, a feature of Sentinel, to obtain files, database or configuration center to set current limiting rules. At present, Apollo, Nacos and ZK are supported for configuration management.

First of all, recall that a current limiting rule is mainly composed of the following factors:

resource: The resource name, that is, the action object of the current limiting rule, that is, value with annotation @ SentinelResource; count: Current limiting threshold; grade: Current limiting threshold type (QPS or number of concurrent threads); limitApp: The call source for flow control. If it is default, the call source is not distinguished; strategy: Current Limiting Strategy Based on Call Relationship; controlBehavior: Flow Control Effect (Direct Deny, Queuing, Uniform Mode)

Understand the meaning, and then configure it through files:


# Read current limiting rules through files 
spring.cloud.sentinel.datasource.ds1.file.file=classpath:flowrule.json
spring.cloud.sentinel.datasource.ds1.file.data-type=json
spring.cloud.sentinel.datasource.ds1.file.rule-type=flow

Create a new file in resources, such as flowrule. json add current limiting rules:


spring:
  application:
    name: gateway-service
  redis: #redis Related configuration 
    database: 8
    host: 10.12.15.5
    port: 6379
    password: 123456 # Set when there is a password 
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
    timeout: 10000ms
4

Restart the project, and the following log appears to indicate success:


spring:
  application:
    name: gateway-service
  redis: #redis Related configuration 
    database: 8
    host: 10.12.15.5
    port: 6379
    password: 123456 # Set when there is a password 
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
    timeout: 10000ms
5

If Nacos is used as the configuration to obtain the current limiting rule, the following configuration can be added to the file:


spring:
  application:
    name: gateway-service
  redis: #redis Related configuration 
    database: 8
    host: 10.12.15.5
    port: 6379
    password: 123456 # Set when there is a password 
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
    timeout: 10000ms
6

Related articles: