资讯专栏INFORMATION COLUMN

spring cloud gateway 之限流篇

joy968 / 2971人阅读

摘要:常见的限流方式,比如适用线程池隔离,超过线程池的负载,走熔断的逻辑。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。,令牌桶每秒填充平均速率。

转载请标明出处: 
https://www.fangzhipeng.com
本文出自方志朋的博客

在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止网络攻击。

常见的限流方式,比如Hystrix适用线程池隔离,超过线程池的负载,走熔断的逻辑。在一般应用服务器中,比如tomcat容器也是通过限制它的线程数来控制并发的;也有通过时间窗口的平均速度来控制流量。常见的限流纬度有比如通过Ip来限流、通过uri来限流、通过用户访问频次来限流。

一般限流都是在网关这一层做,比如Nginx、Openresty、kong、zuul、Spring Cloud Gateway等;也可以在应用层通过Aop这种方式去做限流。

本文详细探讨在 Spring Cloud Gateway 中如何实现限流。

常见的限流算法 计数器算法

计数器算法采用计数器实现限流有点简单粗暴,一般我们会限制一秒钟的能够通过的请求数,比如限流qps为100,算法的实现思路就是从第一个请求进来开始计时,在接下去的1s内,每来一个请求,就把计数加1,如果累加的数字达到了100,那么后续的请求就会被全部拒绝。等到1s结束后,把计数恢复成0,重新开始计数。具体的实现可以是这样的:对于每次服务调用,可以通过AtomicLong#incrementAndGet()方法来给计数器加1并返回最新值,通过这个最新值和阈值进行比较。这种实现方式,相信大家都知道有一个弊端:如果我在单位时间1s内的前10ms,已经通过了100个请求,那后面的990ms,只能眼巴巴的把请求拒绝,我们把这种现象称为“突刺现象”

漏桶算法

漏桶算法为了消除"突刺现象",可以采用漏桶算法实现限流,漏桶算法这个名字就很形象,算法内部有一个容器,类似生活用到的漏斗,当请求进来时,相当于水倒入漏斗,然后从下端小口慢慢匀速的流出。不管上面流量多大,下面流出的速度始终保持不变。不管服务调用方多么不稳定,通过漏桶算法进行限流,每10毫秒处理一次请求。因为处理的速度是固定的,请求进来的速度是未知的,可能突然进来很多请求,没来得及处理的请求就先放在桶里,既然是个桶,肯定是有容量上限,如果桶满了,那么新进来的请求就丢弃。

在算法实现方面,可以准备一个队列,用来保存请求,另外通过一个线程池(ScheduledExecutorService)来定期从队列中获取请求并执行,可以一次性获取多个并发执行。

这种算法,在使用过后也存在弊端:无法应对短时间的突发流量。

令牌桶算法

从某种意义上讲,令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。

实现思路:可以准备一个队列,用来保存令牌,另外通过一个线程池定期生成令牌放到队列中,每来一个请求,就从队列中获取一个令牌,并继续执行。

Spring Cloud Gateway限流

在Spring Cloud Gateway中,有Filter过滤器,因此可以在“pre”类型的Filter中自行实现上述三种过滤器。但是限流作为网关最基本的功能,Spring Cloud Gateway官方就提供了RequestRateLimiterGatewayFilterFactory这个类,适用Redis和lua脚本实现了令牌桶的方式。具体实现逻辑在RequestRateLimiterGatewayFilterFactory类中,lua脚本在如下图所示的文件夹中:

具体源码不打算在这里讲述,读者可以自行查看,代码量较少,先以案例的形式来讲解如何在Spring Cloud Gateway中使用内置的限流过滤器工厂来实现限流。

首先在工程的pom文件中引入gateway的起步依赖和redis的reactive依赖,代码如下:

 
    org.springframework.cloud
    spring-cloud-starter-gateway



    org.springframework.boot
    spring-boot-starter-data-redis-reactive

在配置文件中做以下的配置:

server:
  port: 8081
spring:
  cloud:
    gateway:
      routes:
      - id: limit_route
        uri: http://httpbin.org:80/get
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
        filters:
        - name: RequestRateLimiter
          args:
            key-resolver: "#{@hostAddrKeyResolver}"
            redis-rate-limiter.replenishRate: 1
            redis-rate-limiter.burstCapacity: 3
  application:
    name: gateway-limiter
  redis:
    host: localhost
    port: 6379
    database: 0

在上面的配置文件,指定程序的端口为8081,配置了 redis的信息,并配置了RequestRateLimiter的限流过滤器,该过滤器需要配置三个参数:

burstCapacity,令牌桶总容量。

replenishRate,令牌桶每秒填充平均速率。

key-resolver,用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。

KeyResolver需要实现resolve方法,比如根据Hostname进行限流,则需要用hostAddress去判断。实现完KeyResolver之后,需要将这个类的Bean注册到Ioc容器中。

public class HostAddrKeyResolver implements KeyResolver {

    @Override
    public Mono resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }

}

 @Bean
    public HostAddrKeyResolver hostAddrKeyResolver() {
        return new HostAddrKeyResolver();
    }

可以根据uri去限流,这时KeyResolver代码如下:

public class UriKeyResolver  implements KeyResolver {

    @Override
    public Mono resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getURI().getPath());
    }

}

 @Bean
    public UriKeyResolver uriKeyResolver() {
        return new UriKeyResolver();
    }

 

也可以以用户的维度去限流:

   @Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
    }

用jmeter进行压测,配置10thread去循环请求lcoalhost:8081,循环间隔1s。从压测的结果上看到有部分请求通过,由部分请求失败。通过redis客户端去查看redis中存在的key。如下:

可见,RequestRateLimiter是使用Redis来进行限流的,并在redis中存储了2个key。关注这两个key含义可以看lua源代码。

源码下载

https://github.com/forezp/Spr...

参考资料

http://cloud.spring.io/spring...

https://windmt.com/2018/05/09...

http://www.spring4all.com/art...

扫一扫,支持下作者吧:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72766.html

相关文章

  • Spring Cloud Gateway 扩展支持动态限流

    摘要:以流量为切入点,从流量控制熔断降级系统负载保护等多个维度保护服务的稳定性分布式系统的流量防卫兵。欢迎关注我们获得更多的好玩实践 之前分享过 一篇 《Spring Cloud Gateway 原生的接口限流该怎么玩》, 核心是依赖Spring Cloud Gateway 默认提供的限流过滤器来实现 原生RequestRateLimiter 的不足 配置方式 spring: clou...

    妤锋シ 评论0 收藏0
  • Spring Cloud Gateway 扩展支持动态限流

    摘要:以流量为切入点,从流量控制熔断降级系统负载保护等多个维度保护服务的稳定性分布式系统的流量防卫兵。欢迎关注我们获得更多的好玩实践 之前分享过 一篇 《Spring Cloud Gateway 原生的接口限流该怎么玩》, 核心是依赖Spring Cloud Gateway 默认提供的限流过滤器来实现 原生RequestRateLimiter 的不足 配置方式 spring: clou...

    beanlam 评论0 收藏0
  • Spring Cloud Gateway限流实战

    摘要:欢迎访问我的欢迎访问我的内容所有原创文章分类汇总及配套源码,涉及等本篇概览本篇概览本文是实战系列的第八篇,经过前面的学习,咱们对过滤器已了解得差不多,今天来补全过滤器的最后一个版块限流默认的限流器是基于实现的,限流算法是大家熟悉的令牌桶关于欢迎访问我的GitHubhttps://github.com/zq2599/blog_demos内容:所有原创文章分类汇总及配套源码,涉及Java、Doc...

    stonezhu 评论0 收藏0
  • 阿里Sentinel支持Spring Cloud Gateway

    摘要:应对突发请求时额外允许的请求数目。匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。和为后续参数匹配特性预留,目前未实现。 1. 前言 4月25号,Sentinel 1.6.0 正式发布,带来 Spring Cloud Gateway 支持、控制台登录功能、改进的热点限流和注解 fallback 等多项新特性,该出手时就出手,紧跟时代潮流,昨天刚发布,今天我就要给大家分...

    chengtao1633 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<