转载

Spring Cloud Gateway限流原理

Gateway 在微服务架构系统中,为系统内部服务提供一道安全屏障,提高系统可用性、安全性等问题。使用 Gateway 可帮助开发人员快速开发应用,而不需要关心安全控制、流量控制、审计日志、版本控制等问题。

Spring Cloud Gateway限流原理

为什么需要限流

简单的说限流就是让控制流量让它们合理、平滑、有效的传递到内部服务,而不是随意想进就进,影响系统运行。比如以下一些情况就会使系统流量在短时间内增加从而导致一些意想不到的问题:

  • 用户增长速度快
  • 业务高峰时间段保证业务可用性
  • 应对网络爬虫
  • 防止恶意刷单、非法访问系统
  • ...

上面列出的这些情况是很难预知的,如果某个事件导致服务的流量在很短时间内突然增长太快,在没有限流的情况下会导致服务不可用、系统压力大最终可能会使服务器自动重启(古时候是要杀头的~~)。相反如果有了限流就会好很多流量会得到有效控制,超过阈值的访问直接在网关层就会被拒绝,这样就可以减少服务压力,达到保护内部服务的目的。

限流策略

限制流量也不是随意限制的,限制的不好会适得其反,下面列举几种常见的限流策略:

remote地址 + api
remote地址 + user

策略有很多,选择最合适的才是最好的,就拿 remote ip 来限制来说,比如有几百个人在局域网内共享一个公网IP,系统每秒限制10个请求那这几百人在使用你们应用的时候经常会被拒绝,那体验也太差了,这种情况如果按 user 来限制每个用户都能每秒发10个请求,再给 api 加个总的限制也许就会比 remote 策略好很多。所以在限制流量的时候也要考虑策略方面的因素,选择最适合业务场景的策略。

Spring Cloud Gateway限流原理

使用 Spring Cloud Gateway 后,如果进行流量管理呢?下面我们就来探讨下如何使用 Spring Cloud Gateway 进行流量管理,先简单了解下 Spring Cloud Gateway 的工作原理:

Spring Cloud Gateway限流原理

经过上面图不难发现,流量会依次进入:

Gateway Handler Mapping
Gateway Web Handler
Filter

到达 Proxied Service 后的请求如何处理 Gateway 就不管了,处理完成之后请求又回到 Gateway 做后处理。搞清楚请求的走向,我们就可以在请求处理之前和之后对请求做一些不可描述的事了!!。

RequestRateLimiterGatewayFilterFactory 过滤器

Spring Cloud Gateway 很多内置的 Filter ,如: AddRequestHeaderAddRequestParameter 等等,这些都可以直接拿来使用,只需要简单配置参数即可。本文只关注 RequestRateLimiter 内置的过滤器来完成限流。首先写一个测试接口:

package com.example.gateway;

@RestController
@RequestMapping("/traffic")
public class TrafficController {


    @GetMapping
    public ResponseEntity<String> testTrafficLimit(){
        return ResponseEntity.ok("Success");
    }
}
复制代码

然后再配置下路由:

spring:
  cloud:
    gateway:
      routes:
      - id: traffic_litmit
        uri: http://localhost:8080
        predicates:
         - Path=/traffic_route
        filters:
         - RewritePath=/traffic_route, /traffic
         - name: RequestRateLimiter
           args:
            rate-limiter: "#{@defaultRateLimiter}"
            key-resolver: "#{@hostAddKeyResolver}"
复制代码

上面的配置添加一个名称为 RequestRateLimiter 的滤器的, RequestRateLimiter 可以理解为一个过滤器工厂的名称, Spring Cloud Gateway 是通过 GatewayFilter Factories 去实现过滤器的, RequestRateLimiter 与之对应的工厂类为 RequestRateLimiterGatewayFilterFactory ,那 RequestRateLimiterGatewayFilterFactory 是如何工作呢?

Spring Cloud Gateway限流原理

结合上面的图,简单的说差不多就二步:

  1. 获取key
  2. 判断是否允许访问

key是通过调用 KeyResolver 实例来获取的,判断是否允许访问是通过调用 RateLimiter 来决定的。分工还是很明确的,所以要使用 Spring Cloud Gateway 限流只需要实现这两个接口再加上过滤器配置就可以啦。

使用Spring Cloud Gateway限流

上面已经大致介绍过 Spring Cloud Gateway 限流的一些知识,再结合 Guava 来做个简单的 RateLimiter 来演示下限流的使用,话不多说上代码:

public class DefaultRateLimiter extends AbstractRateLimiter<DefaultRateLimiter.Config> {

    Logger logger = LoggerFactory.getLogger(DefaultRateLimiter.class.getName());

    /**
     * 默认一秒一个请求,多了不收了~~
     */
    private final RateLimiter limiter = RateLimiter.create(1);


    public DefaultRateLimiter() {
        super(Config.class, "default-rate-limit", null);
    }

    @Override
    public Mono<Response> isAllowed(String routeId, String id) {
        Config config = getConfig().get(routeId);
        limiter.setRate(Objects.isNull(config.getPermitsPerSecond()) ? 1 : config.getPermitsPerSecond());

        logger.info("Rate: {}", limiter.getRate());
        boolean isAllow = limiter.tryAcquire();

        logger.info("isAllow: {} , time:  {}",isAllow,  System.currentTimeMillis());
        return Mono.just(new Response(isAllow, new HashMap<>()));
    }

    @Validated
    public static class Config {

        @DecimalMin("0.1")
        private Double permitsPerSecond;


        public Double getPermitsPerSecond() {
            return permitsPerSecond;
        }

        public Config setPermitsPerSecond(Double permitsPerSecond) {
            this.permitsPerSecond = permitsPerSecond;
            return this;
        }
    }
}
复制代码

再修改一下过滤器的配置即可完成限流功能:

filters:
 - RewritePath=/traffic_route, /traffic
 - name: RequestRateLimiter
   args:
    rate-limiter: "#{@defaultRateLimiter}"
    key-resolver: "#{@hostAddKeyResolver}"
    default-rate-limit.permitsPerSecond: 0.5

复制代码

这样就完成一个自定义的 RateLimiter 了, 如果没有什么特殊要求的话可以直接使用 RedisRateLimiter 来实现限流,这个是内置限流器只需要简单配置两个参数就能使用:

filters:
 - StripPrefix=1
 - name: RequestRateLimiter
   args:
    redis-rate-limiter.replenishRate: 1
    redis-rate-limiter.burstCapacity: 2
复制代码

参数说明:

  • redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求
  • redis-rate-limiter.burstCapacity:令牌桶的容量,允许在一秒钟内完成的最大请求数
原文  https://juejin.im/post/5da271766fb9a04e20417b4d
正文到此结束
Loading...