The background,

In our usual development process, A request generally needs to pass through multiple microservices, ** for example: ** requests flow from A service to B service, if A service request is too fast, resulting in slow response of B service, then it will inevitably lead to system problems. Because we need finite flow operations.

Two, to achieve the function

  1. Provides custom flow limiting key generation, needs to be implementedKeyResolverInterface.
  2. Provides the default traffic limiting algorithm, implementationRateLimiterInterface.
  3. When the key of the flow limiting is empty, the traffic is directly not restricted, and is released by the parameterspring.cloud.gateway.routes[x].filters[x].args[x].deny-empty-keyTo control the
  4. The corresponding code returned to the client during traffic limiting isspring.cloud.gateway.routes[x].filters[x].args[x].status-codeTo control, you need to write thisorg.springframework.http.HttpStatusClass enumeration value.
  5. RequestRateLimiterCan only usename || argsThis configuration mode cannot be configured in shorthand mode.

  1. RequestRateLimiterThe filterredis-rate-limiterParameter is inRedisRateLimitertheCONFIGURATION_PROPERTY_NAMEProperty configured. Constructor method.

Traffic limiting at the gateway layer

The key generation rule for traffic limiting is PrincipalNameKeyResolver and RedisRateLimiter, which is the token bucket algorithm by default.

1. Use the default redis to limit traffic

The RequestRateLimiter filter is provided by default in the Spring Cloud Gateway for traffic limiting.

1. Introduce jar packages

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
Copy the code

2. Write a configuration file

spring:
  application:
    name: gateway-9205
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8847
    gateway:
      routes:
        - id: user-provider-9206
          uri: lb://user-provider-9206
          predicates:
            - Path=/user/**
          filters:
            - RewritePath=/user(? 
      
       /? . *),
       $\{segment}
            - name: RequestRateLimiter
              args:
                If the key returned is empty, no limiting is performed
                deny-empty-key: false
                # How many tokens are generated per second
                redis-rate-limiter.replenishRate: 1
                # The maximum token in 1 second, that is, the burst flow that can be allowed in 1s, is set to 0 to block all requests
                redis-rate-limiter.burstCapacity: 1
                Request several tokens per request
                redis-rate-limiter.requestedTokens: 1
  redis:
    host: 192.1687.1.
    database: 12
    port: 6379
    password: 123456

server:
  port: 9205
debug: true
Copy the code

3. The gateway responds normally

4. Gateway traffic limiting response

2. Customize the traffic limiting algorithm and key

1. Customize traffic limiting keys

Write a class that implements the KeyResolver interface.

package com.huan.study.gateway;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Optional;

/** * The key for limiting traffic gets **@authorHuan. Fu 2021 1/9/7-10:25 am */
@Slf4j
@Component
public class DefaultGatewayKeyResolver implements KeyResolver {

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // Get the current route
        Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);

        ServerHttpRequest request = exchange.getRequest();
        String uri = request.getURI().getPath();
        log.info("Currently returned URI :[{}]", uri);

        return Mono.just(Optional.ofNullable(route).map(Route::getId).orElse("") + "/"+ uri); }}Copy the code

Writing in the configuration file (part)

spring:
  cloud:
    gateway:
      routes:
        - id: user-provider-9206
          filters:
            - name: RequestRateLimiter
              args:
                # return the key that limits the flow
                key-resolver: "#{@defaultGatewayKeyResolver}"
              
Copy the code

2. Custom traffic limiting algorithm

Write a class to implement RateLimiter, where memory limiting is used

package com.huan.study.gateway;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter;
import org.springframework.cloud.gateway.support.ConfigurationService;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/ * * *@authorHuan. Fu 2021 1/9/7-10:36 am */
@Component
@Slf4j
@Primary
public class DefaultGatewayRateLimiter extends AbstractRateLimiter<DefaultGatewayRateLimiter.Config> {

    /** * corresponds to the configuration properties in the configuration file */
    private static final String CONFIGURATION_PROPERTY_NAME = "default-gateway-rate-limiter";

    private RateLimiter rateLimiter = RateLimiter.create(1);

    protected DefaultGatewayRateLimiter(ConfigurationService configurationService) {
        super(DefaultGatewayRateLimiter.Config.class, CONFIGURATION_PROPERTY_NAME, configurationService);
    }

    @Override
    public Mono<Response> isAllowed(String routeId, String id) {
        log.info("Default gateway routeId:[{}], ID :[{}]", routeId, id);

        Config config = getConfig().get(routeId);

        return Mono.fromSupplier(() -> {
            boolean acquire = rateLimiter.tryAcquire(config.requestedTokens);
            if (acquire) {
                return new Response(true, Maps.newHashMap());
            } else {
                return new Response(false, Maps.newHashMap()); }}); }@Getter
    @Setter
    @ToString
    public static class Config {
        /** * How many tokens are requested each time */
        privateInteger requestedTokens; }}Copy the code

Writing in the configuration file (part)

spring:
  cloud:
    gateway:
      routes:
        - id: user-provider-9206
          filters:
            - name: RequestRateLimiter
              args:
                # Customize traffic limiting rules
                rate-limiter: "#{@defaultGatewayRateLimiter}"
Copy the code

Note ⚠️ : this class requires the @primary annotation.

3. Write in the configuration file

spring:
  application:
    name: gateway-9205
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8847
    gateway:
      routes:
        - id: user-provider-9206
          uri: lb://user-provider-9206
          predicates:
            - Path=/user/**
          filters:
            - RewritePath=/user(? 
      
       /? . *),
       $\{segment}
            - name: RequestRateLimiter
              args:
                # Customize traffic limiting rules
                rate-limiter: "#{@defaultGatewayRateLimiter}"
                # return the key that limits the flow
                key-resolver: "#{@defaultGatewayKeyResolver}"
                If the key returned is empty, no limiting is performed
                deny-empty-key: false
                Response code 429: too many requests
                status-code: TOO_MANY_REQUESTS
                # each request to apply for a few token default gateway - rate - value is defined in defaultGatewayRateLimiter limiter.
                default-gateway-rate-limiter.requestedTokens: 1
server:
  port: 9205
debug: true
Copy the code

Finish the code

Gitee.com/huan1993/sp…

5. Reference documents

1, the docs. Spring. IO/spring – clou…