This is the 8th day of my participation in Gwen Challenge. This article is participating in “Java Theme Month – Java Development in Action”, see the activity link for details.

The service can sometimes come under a lot of pressure from frequent network requests, especially from illegal network attacks. Such situations sometimes require some restrictions. For example, to restrict the request of the other party, this restriction can be based on several factors: request IP address, user unique identifier, requested interface address, and so on.

At present, there are many ways to limit traffic: Spring Cloud has some functions of limiting traffic in the gateway itself, which are based on Redis. Alibaba has also opened an open-source version of its Sentinel stream limiting device. Today we mainly focus on these two actual practice of micro-service flow limiting mechanism.

First, we will talk about the native flow limiting function of Spring Cloud, because flow limiting can be applied to each service or uniformly to the gateway.

I. Actual combat Based on the flow limiting of Spring Cloud Gateway

Pom.xml introduces dependencies:

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

Its foundation is based on Redis, so:

Database: 8 host: 10.12.15.5 port: 6379 password: Jedis: pool: max-active: 8 max-idle: 8 min-idle: 0 timeout: 10000ms jedis: pool: max-active: 8 max-idle: 8 min-idle: 0 timeout: 10000msCopy the code

Next we need to inject the bean with the traffic limiting policy:

@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"));
  }
Copy the code

There are three policies: ipKeyResolver, apiKeyResolver, and userKeyResolver. You can use the @primary annotation to determine which one is used.

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

Database: 8 host: 10.12.15.5 port: 6379 password: Jedis: pool: max-active: 8 max-idle: 8 min-idle: 0 timeout: 10000ms jedis: pool: max-active: 8 max-idle: 8 min-idle: 0 timeout: 10000msCopy the code

The following are the main configurations of traffic limiting:

Spring Cloud: gateway: routes: # List: cas-server: uri: lb://cas-server-service: Predicates: -path =/cas-server/** # Predicates: -path =/cas-server/** # /ribbon - name: RequestRateLimiter # Route limiting args for the Redis-based Gateway: ReplenishRate: 1 # replenishRate: number of requests processed per second by the user redis-rate-limiter. BurstCapacity: replenishRate: 1 # replenishRate: replenishRate: 1 # replenishRate: number of requests processed per second by the user redis-rate-limiter. #{@ipkeyresolver}" #{@ipkeyresolver}" lb://admin-web-service order: -1 predicates: - Path=/admin-web/** filters: - StripPrefix=1 - name: RequestRateLimiter ARgs: Redis-rate-limiter. ReplenishRate: 1 # The number of requests processed per second by the user redis-rate-limiter. BurstCapacity: 3 # The capacity of the token bucket, the maximum number of requests that can be completed in one secondCopy the code

Add the RequestRateLimiter flow limiting filter to the original route and include three parameters:

- name: RequestRateLimiter # replenishRate: redis-rate-limiter. ReplenishRate: 3 # Redis-rate-limiter. burstCapacity: 5 # The capacity of the token bucket, the maximum number of requests that can be completed in one second "#{@ipKeyresolver}" #SPEL expression takes the corresponding beanCopy the code
  • ReplenishRate, which refers to the number of requests processed per second;
  • BurstCapacity indicates the maximum number of requests that can be processed in one second.
  • In key-resolver, request IP flow limiting is adopted, and the corresponding bean is obtained by SPEL expression

Write a small script to test it:

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; doneCopy the code

The above two scripts pressure test the two services respectively 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_ad min\",\"username\":\"admin\",\"realName\":\"super_admin\",\"password\":\"$2a$10$89AqlYKlnsTpNmWcCMvgluRFQ/6MLK1k/nkBpz.L w6Exh.WMQFH6W\",\"phone\":null,\"email\":null,\"createBy\":\"admin\",\"createTime\":1573119753172,\"updateBy\":\"admin\" ,\"updateTime\":1573119753172,\"loginTime\":null,\"expireTime\":null,\"remarks\":\"super_admin\",\"delFlag\":0,\"loginTy pe\":null}}"}exCopy the code

After multiple requests in the same second using the testing tool Jmeter:

HTTP/1.1 429 Too Many Requests X-Ratelimit-Remaining: 0 X-Ratelimit-burst-Capacity: 3 x-Ratelimit-plen-Rate: 1 Content-Length: 0 expr: syntax error HTTP/1.1 429 Too Many Requests X-ratelimit-Remaining: 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 errorCopy the code

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

Second, Based on Ali open source stream limiting artifact: Sentinel

First we introduce dependencies:

<! <dependency> <groupId>com.alibaba. Cloud </groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>Copy the code

Yaml configuration file, you need to add two new configurations:

spring: application: name: admin-web cloud: kubernetes: discovery: all-namespaces: true sentinel: eager: Transport: dashboard: 10.12.15.2:8080 8719 # Is the sentinel application and console communication port heartbeat-interval-ms: 500 # heartbeat time SCG: fallback: # SCG. Response Response-status: 455 Response-body: traffic is restrictedCopy the code

Among them, there is configured with a service: spring. Cloud. Sentinel. Transport. The dashboard, the configuration is the address of the dashboard sentinel. At the same time the spring. Cloud. Sentinel. Transport. The port of the port configuration in the application of equivalent machine start an Http Server, the Server will make interaction with sentinel console.

Sentinel provides traffic limiting burying points for all HTTP services by default. After the preceding configuration is complete, all burying points are automatically completed. You only need to configure traffic limiting rules.

Here’s how to annotate a specified interface function to make limiting points. Write a RestController, and make a comment on the interface function @sentinelResource:

@GetMapping(value = "/getToken")
@SentinelResource("getToken")
public Response<Object> getToken(Authentication authentication){
    //Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    authentication.getCredentials();
    OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();
    String token = details.getTokenValue();
    return Response.ok(200, 0, "get token success", token);
}
Copy the code

Now that the code is complete, install SentinelDashBoard. SentinelDashBoard can be downloaded at github.com/alibaba/Sen… .

When the download is complete, the command starts:

Java jar sentinel - dashboard - 1.6.2. JarCopy the code

The default startup port is 8080, and the access IP address is 8080. The Sentinel login page is displayed. The user name and password are both Sentinel. After logging in to the Dashboard, you can access /getToken for several times. The corresponding data is displayed on the Dashboard, which is not displayed here. Then, you can set the traffic limiting function of the interface. Click the “+ Flow Control” button to open the setting interface, and set the threshold type to QPS and single-node threshold to 5.

Browser to repeat request http://10.10.15.5:5556/admin-web/api/user/getToken if more than the threshold will appear interface information is as follows:

Blocked by Sentinel (flow limiting)
Copy the code

At this point, see Sentinel current-limiting effect, can add spring. Cloud. Sentinel. SCG. The fallback to Sentinel current-limiting response after configuration, also can custom current-limiting exception information:

@GetMapping(value = "/getToken") @SentinelResource(value = "getToken", blockHandler = "handleSentinelException", blockHandlerClass = {MySentinelException.class})) public Response<Object> getToken(Authentication authentication){ //Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); authentication.getCredentials(); OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails(); String token = details.getTokenValue(); return Response.ok(200, 0, "get token success", token); } public class MySentinelException { public static Response<Object> handleSentinelException(BlockException e) { Map<String,Object> map=new HashMap<>(); logger.info("Oops: " + ex.getClass().getCanonicalName()); Return response. ok(200, -8, "Make @sentinelResource to configure and customize the processing logic after limiting traffic ", null); }}Copy the code

The @sentinelResource annotation contains the following properties:

  • Value: indicates the resource name, which is required.
  • EntryType: entryType, optional (default entrytype.out).
  • BlockHandler: specifies the name of the exception handling method corresponding to the blockHandlerClass. The parameter types and return values must be the same as those of the original method.
  • BlockHandlerClass: custom flow limiting logic processing class

The Sentinel traffic limiting logic was processed, but the traffic limiting rules were cleared after each service restart. Because it’s a regular object in memory. ReadableDataSource, a feature of Sentinel, can be used to obtain files, databases, or configuration centers to set traffic limiting rules.

First of all, recall that a traffic limiting rule consists of the following factors:

  • Resource: The name of the resource, the object of the traffic limiting rule, the value of @sentinelResource;
  • Count: traffic limiting threshold. Grade: indicates the type of traffic limiting threshold (QPS or number of concurrent threads).
  • LimitApp: call source for flow control. If it is default, call source is not distinguished.
  • Strategy: Traffic limiting strategy based on call relationship;
  • ControlBehavior: Flow control effect (direct reject, queuing, uniform mode)

Now that you understand the meaning, you can configure it through the file:

# current limiting rules spring. Read through the file cloud. Sentinel. The datasource. An file. The file = classpath: flowrule. Json spring.cloud.sentinel.datasource.ds1.file.data-type=json spring.cloud.sentinel.datasource.ds1.file.rule-type=flowCopy the code

Create a new file in Resources, such as flowrule-json, and add the flow limiting rule:

[
  {
    "resource": "getToken",
    "count": 1,
    "controlBehavior": 0,
    "grade": 1,
    "limitApp": "default",
    "strategy": 0
  },
  {
    "resource": "resource",
    "count": 1,
    "controlBehavior": 0,
    "grade": 1,
    "limitApp": "default",
    "strategy": 0
  }
]
Copy the code

The project is restarted if the following log is displayed:

DataSource ds1-sentinel-file-datasource start to loadConfig
DataSource ds1-sentinel-file-datasource load 2 FlowRule
Copy the code

If Nacos is used as the configuration to obtain traffic limiting rules, you can add the following configuration to the file:

Spring: Application: name: order-service cloud: nacos: config: server-addr: 10.10.15.5:8848 Discovery: server-addr: 10.10.15.5:8848 Sentinel: eager: true Transport: Dashboard: 10.10.15.5:8080 datasource: DS1: nacos: server-addr: 10.10.15.5:8848 dataId: ${spring.application.name}-flow-rules data-type: json rule-type: flowCopy the code