Gateway服务限流配置

Gateway服务限流配置

背景

限流作用就不说了,往往都是防止一些恶意请求,无限制请求接口导致服务处理时间过长,继而导致响应延迟,服务阻塞等等,所以会对高频率的一些接口添加限流这样的功能。

刚好gateway刚好自带了服务限流功能,所以我们就用gateway实现此功能。

编写一个限流规则

1、添加RateLimiterConfig配置文件

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、编写配置文件让配置文件扫描刚刚添加的bean

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}"

注意:

  • 那个服务需要限流就在那个服务下加入filter的配置即可,如果不需要限流则不需要添加
  • name字段必须为RequestRateLimiter
  • key-resolver参数对应注入到Spring中Bean的名称。

但是我们发现请求后如果用户请求接口到达上限制返回的是1,这让前端不知道是后端发生了什么异常,这并不符合我们的设计要求。

所以服务升级

​ |

​ |

​ |

​ \|/

自定义返回信息配置类

添加GatewayRequestRateLimiterGatewayFilterFactory自定义异常类

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;
  }
}

接着修改我们的配置文件

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}"

最后我们的限流工作就ok了。

image-20230515211619195

温馨小提示:gateway默认的限流规则只能有一个,如果需要配置两个可以参考–>(3条消息) Spring Cloud Gateway 限流适配多规则的解决方案_Suremotoo丶小小书童的博客-CSDN博客的文章。

猜你喜欢

转载自blog.csdn.net/m0_57647880/article/details/130693077