目录

一、常见限流场景

1.1 限流对象

1.2 限流的处理

二、限流算法

2.1 计数器算法

2.2 漏桶算法(Leaky Bucket)

2.3 令牌桶算法(Token Bucket)

2.3 总结三种算法特点

三、Gateway网关限流

3.1 添加依赖jar包

3.2 添加配置

3.3 定义KeyResolver的bean对象

3.4 测试

3.5 限流的源码

 四、参考资料


一、常见限流场景

缓存、降级、限流被称为高并发、分布式系统中经常提及的话题。这里讨论网关限流。

1.1 限流对象

a):请求频率限流(Request rate limiting):限制某个接口的一分钟内的请求数

b):并发量限流(Concurrent requests limiting):限制某个服务的处理请求数

c):传输速率限流(Transmission rate limiting):限制下载文件速率

d):限制黑名单用户访问

e):限制某个IP请求

1.2 限流的处理

a):拒绝服务:请求直接抛出异常,如:gateway返回429状态码

b):排队等待:请求放入队列中,等待处理

c):服务降级:请求返回兜底数据等

二、限流算法

2.1 计数器算法

如上图所示,假定1min内限制请求数量为100,一个请求则计数器counter+1。当counter大于100且还在当前的1min内,触发限流;1min过后,counter重新计数。计数器算法存在临界问题,如下图所示:假设有一个恶意用户,他在 0:59 时,瞬间发送了 100 个请求,并且 1:00 又瞬间发送了 100 个请求,那么其实这个用户在 1 秒里面,瞬间发送了 200 个请求。这个临界点上,可以压垮服务。

 

2.2 漏桶算法(Leaky Bucket)

桶的上面是个水龙头,请求从水龙头流到桶中,水龙头流出的水速不定,有时快有时慢,这种忽快忽慢的流量叫做 Bursty flow。如果桶中水满,多余的水就会溢出,即请求被丢弃。从桶底部漏出的水速是固定的,可以看出漏桶算法可以平滑请求的速率。对于漏桶算法,桶的容量不变,流出的速率不变,其底层结构是个队列(如下图所示)。当请求到来时,不是直接处理,而是放入队列中,另一个线程以固定的速率从队列读取请求,从而达到限流的目的。

基于漏桶算法的特点,该算法主要用途是保护服务,无法处理突发流量,而流出速率缓慢,可能造成服务资源的浪费。

2.3 令牌桶算法(Token Bucket)

令牌桶算法是应用最广泛的限流算法,主要有两部分组成:生产令牌、消费令牌。

  • 生产令牌:固定容量的令牌桶,按固定的速率(N/s)往桶中放入令牌,桶满时不再放入;
  • 消费令牌:每个请求需要从桶中拿取令牌,当消费速率低于生产速率时,直至桶中令牌满而触发限流,此时请求可以放入缓冲队列或直接拒绝。

令牌桶算法有一个很关键的问题,就是桶容量的设置,这个参数可以让令牌桶算法具备处理突发流量的能力。假如将桶容量设置为 100,生成令牌的速度为每秒 10 个,那么在系统空闲一段时间之后(桶中令牌一直没有消费,慢慢的会被装满),突然来了 50 个请求,这时系统可以直接按每秒 50 个的速度处理,随着桶中的令牌很快用完,处理速度又会慢慢降下来,和生成令牌速度趋于一致。这是令牌桶算法和漏桶算法最大的区别,漏桶算法无论来了多少请求,只会按固定速度进行处理。

2.3 总结三种算法特点

类别特点缺点
计数器算法

1.结构简单-计数器;

2.临界问题;

出现临界问题
漏斗算法

1.固定速率处理请求;

2.保护服务;

无法处理突发流量
令牌桶算法

1.固定速率生产令牌;

2.设置容量大小;

3.处理突发流量;

容量设置不合理,可能压垮服务

 

三、Gateway网关限流

3.1 添加依赖jar包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

3.2 添加配置

spring:
  redis:
    host: 127.0.0.1
    port: 6379
  cloud:
    gateway:
      # gateway发现该eureka下的所有服务
      discovery:
        locator:
          enabled: false
          lowerCaseServiceId: true
      default-filters:
        - name: Hystrix
          args:
            name: default
            fallbackUri: 'forward:/fallback/global'
      routes:
        - id: ${application.name.instance-test}
          predicates:
            - Path=/${application.name.instance-test}/**
          uri: lb://${application.name.instance-test}
          filters:
            - name: RequestRateLimiter #官方给的名称,无需改动
              args:
                redis-rate-limiter.replenishRate: 50   #令牌桶填充速率,每秒50个
                redis-rate-limiter.burstCapacity: 100  #令牌桶容量,100个
                key-resolver: "#{@apiKeyResolver}"     #SpEL表达式从Spring容器中获取Bean对象

3.3 定义KeyResolver的bean对象

配置中key-resolver: "#{@apiKeyResolver}",其中apiKeyResolver对应的是下面方法的名称

    // 定义KeyResolver,限制api接口
    @Bean
    public KeyResolver apiKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getPath().value());
    }

3.4 测试

a. redis是否有key

b. 限制请求后,网关直接返回429状态码

3.5 限流的源码

a.RedisRateLimiter的类图

配置过滤器中:- name: RequestRateLimiter,指的是下图类图的实现类,可以自定义限流策略需要继承AbstractRateLimiter,核心代码是org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter#isAllowed方法

b. 实现限流算法的Lua脚本

request_rate_limiter.lua脚本,位置如下图所示。

 四、参考资料

https://www.imooc.com/article/290828

https://blog.csdn.net/weixin_36384501/article/details/113073344

https://blog.csdn.net/weixin_34019929/article/details/88023005

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐