Spring Cloud Gateway限流实现

一,引入依赖
采用redis的令牌桶算法实现

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

二、指定KeyResolver
我们采用IP来做限流;也可以获取request中的用户,根据用户限流;根据接口限流等实现

@SpringBootApplication
@EnableEurekaClient
public class BaseGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(BaseGatewayApplication.class);
    }


    @Bean(name = "ipKeyResolver")
    public KeyResolver ipKeyResolver() {
        return new KeyResolver() {
            @Override
            public Mono<String> resolve(ServerWebExchange exchange) {
                String hostName = exchange.getRequest().getRemoteAddress().getHostName();
                System.out.println("hostName:" + hostName);
                return Mono.just(hostName);
            }
        };
    }
}

三、配置限流过滤器
filters: 名称默认是 RequestRateLimiter,可以自定义后面会使用自定义的过滤器
redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求。
redis-rate-limiter.burstCapacity:令牌桶的容量
key-resolver:自己定义的KeyResolver的bean对象

spring:
  application:
    name: ztx-basegateway
  redis:
    host: 192.168.252.128
    port: 6379
  cloud:
    gateway:
      globalcors:
        cors-configurations: # 全局允许跨域访问
          '[/**]':
            allow-credentials: true
            allowed-origins: "*"
            allowed-headers: "*"
            allowed-methods:
              - OPTIONS
              - GET
              - POST
              - PUT
              - DELETE
      routes:
        - id: ztx-basemybatis
          uri: lb://ztx-basemybatis
          predicates:
           - Path=/user/**
          filters:
            - name: RequestRateLimiter #请求数限流
              args:
                key-resolver: "#{@ipKeyResolver}"
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 1

四、自定义限流过滤器
以上就实现了gateway限流,但返回结果不利于前端处理,因此这里我们自定义一个限流过滤器对返回结果进行处理。我们需要继承RequestRateLimiterGatewayFilterFactory类,重写apply(RequestRateLimiterGatewayFilterFactory.Config config)的一个方法,具体见该类的源码,这里简单处理一下。

@Component
public class TestRequestRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {

    private final RateLimiter defaultRateLimiter;

    private final KeyResolver defaultKeyResolver;

    public TestRequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, @Qualifier("ipKeyResolver") KeyResolver defaultKeyResolver) {
        super(defaultRateLimiter, defaultKeyResolver);
        this.defaultRateLimiter = defaultRateLimiter;
        this.defaultKeyResolver = defaultKeyResolver;
    }


    @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 -> {
            Route route = (Route)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            String routeId = route.getId();
            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);
                }

                System.out.println("限流:" + routeId);
                JSONObject resultJson = new JSONObject();
                resultJson.put("message", "访问限制,请稍候重试");
                resultJson.put("status", 20001);
                byte[] bytes = JSONObject.toJSONBytes(resultJson);
                ServerHttpResponse httpResponse = exchange.getResponse();
                httpResponse.setStatusCode(config.getStatusCode());
                if (!httpResponse.getHeaders().containsKey("Content-Type")) {
                    httpResponse.getHeaders().add("Content-Type", "application/json");
                }
                DataBuffer buffer = httpResponse.bufferFactory().wrap(bytes);
                return httpResponse.writeWith(Flux.just(buffer));
            });
        });
    }

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

修改配置采用自定义的过滤器TestRequestRateLimiter

          filters:
            - name: TestRequestRateLimiter #RequestRateLimiter
              args:
                key-resolver: "#{@ipKeyResolver}"
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 1

以上就完成了gateway限流的实现,通过测试返回可以现实

{
    "message": "访问限制,请稍候重试",
    "status": 20001
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值