Gateway service current limiting configuration

Gateway service current limiting configuration

background

Not to mention the role of current limiting, it is often used to prevent some malicious requests. Unlimited request interfaces lead to long service processing time, which in turn leads to response delays, service blocking, etc., so some high-frequency interfaces will be added. Function.

It just so happens that the gateway has its own service flow limiting function, so we use the gateway to realize this function.

Write a throttling rule

1. Add RateLimiterConfig configuration file

package org.example.config;

import lombok.extern.slf4j.Slf4j;
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 reactor.core.publisher.Mono;

@Configuration
@Slf4j
public class RateLimiterConfig {
    
    

    RateLimiterConfig(){
    
    
        log.info("限流配置加载");
    }

    /**
     * 根据用户ip限流规则
     * @return
     */
    @Primary
    @Bean(value = "remoteAddrKeyResolver")
    public KeyResolver remoteAddrKeyResolver(){
    
    
        return exchange -> {
    
    
            String hostAddress=exchange.getRequest()
                    .getRemoteAddress()
                    .getAddress()
                    .getHostAddress();
            System.out.println(hostAddress);
            return Mono.just(hostAddress);
        };
    }
    /**
     * 根据Path限流
     *
     * @return key
     */
    @Bean(value = "pathKeyResolver")
    public KeyResolver pathKeyResolver() {
    
    
        return exchange -> Mono.just(exchange.getRequest().getPath().toString());
    }


}

2. Write a configuration file to let the configuration file scan the bean just added

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false
      routes:
        # 商品模块
        - id: Commodity #路由id,唯一
          uri: lb://Commodity
          predicates:
            - Path=/commodity/** #以city开头的请求都负载到consumer服务
          filters:
            - name: RequestRateLimiter
              args:
                # 令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 5
                # 令牌桶的上限
                redis-rate-limiter.burstCapacity: 10
                key-resolver: "#{@remoteAddrKeyResolver}"

Notice:

  • If the service needs to limit the flow, just add the filter configuration under that service. If you don’t need the flow limit, you don’t need to add it.
  • The name field must beRequestRateLimiter
  • The key-resolver parameter corresponds to the name of the Bean injected into Spring.

But we found that if the user requests the interface to reach the upper limit after the request, the return value is 1, which makes the front end not know what happened to the back end, which does not meet our design requirements.

so service upgrade

​ |

​ |

​ |

​ \|/

Custom return information configuration class

Add GatewayRequestRateLimiterGatewayFilterFactory custom exception class

package org.example.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Map;

/***
 * @限流自定义
 * @author CMS
 */
@Slf4j
@Component
public class GatewayRequestRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {
    
    


  private final RateLimiter defaultRateLimiter;

  private final KeyResolver defaultKeyResolver;

  public GatewayRequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {
    
    
    super(defaultRateLimiter, defaultKeyResolver);
    this.defaultRateLimiter = defaultRateLimiter;
    this.defaultKeyResolver = defaultKeyResolver;
    log.info("限流自定义返回加载");
  }

  @Override
  public GatewayFilter apply(Config config) {
    
    
    KeyResolver resolver = getOrDefault(config.getKeyResolver(), defaultKeyResolver);
    RateLimiter<Object> limiter = getOrDefault(config.getRateLimiter(), defaultRateLimiter);
    return (exchange, chain) -> resolver.resolve(exchange).flatMap(key -> {
    
    
      String routeId = config.getRouteId();
      if (routeId == null) {
    
    
        Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        routeId = route.getId();
      }
      String finalRouteId = routeId;
      return limiter.isAllowed(routeId, key).flatMap(response -> {
    
    
        for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
    
    
          exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
        }
        if (response.isAllowed()) {
    
    
          return chain.filter(exchange);
        }
        log.warn("已限流: {}", finalRouteId);
        ServerHttpResponse httpResponse = exchange.getResponse();
        //修改code为500
        httpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        if (!httpResponse.getHeaders().containsKey("Content-Type")) {
    
    
          httpResponse.getHeaders().add("Content-Type", "application/json");
        }
        //此处无法触发全局异常处理,手动返回
        DataBuffer buffer = httpResponse.bufferFactory().wrap(("{\n"
            + "  \"code\": \"1414\","
            + "  \"message\": \"服务器限流\","
            + "  \"data\": \"Server throttling\","
            + "  \"success\": false"
            + "}").getBytes(StandardCharsets.UTF_8));
        return httpResponse.writeWith(Mono.just(buffer));
      });
    });
  }

  private <T> T getOrDefault(T configValue, T defaultValue) {
    
    
    return (configValue != null) ? configValue : defaultValue;
  }
}

Then modify our configuration file

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false
      routes:
        # 商品模块
        - id: Commodity #路由id,唯一
          uri: lb://Commodity
          predicates:
            - Path=/commodity/** #以city开头的请求都负载到consumer服务
          filters:
            - name: GatewayRequestRateLimiter  #修改此处,改成我们自定义的返回信息配置类
              args:
                # 令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 5
                # 令牌桶的上限
                redis-rate-limiter.burstCapacity: 10
                key-resolver: "#{@remoteAddrKeyResolver}"

Finally, our current limiting work is ok.

image-20230515211619195

Reminder: Gateway can only have one default current limiting rule, if you need to configure two, you can refer to –> (3 messages) Spring Cloud Gateway current limiting adaptive multi-rule solution CSDN blog article.

Guess you like

Origin blog.csdn.net/m0_57647880/article/details/130693077