常见的限流算法

ywz888
发布于 2022-11-2 10:15
浏览
0收藏

在高并发环境下,为了保证系统的稳定,通常会用到限流、降级、熔断等手段,来保证系统的稳定可用。

限流顾名思义就是限制服务处理的流量,其实熔断、降级本质上也是限流的一种,都是阻断了请求流量,本篇文章重点介绍常见的限流算法。

为什么限流

为什么需要限流呢?这个问题比较好理解,就是请求服务的流量过大,会导致服务崩溃,为了避免这种情况的发生,所以要对流量进行限制。

在以下这些常见的情况下可能会引起流量激增:

  • 促销活动
  • 恶意用户刷单或请求服务
  • 网络爬虫
  • 正常的用户量增大

这些情况都会导致流量的增加,加入我们服务的QPS最大只能支持到1000,当某一时刻的请求增大到1000以上时,我们就要对流量限制在1000以内,这样才能保证服务的稳定运行,这就是限流。

漏桶算法

漏桶算法思路比较简单,就好比有一个漏斗,只能按照漏斗口的大小往外出水,当流入漏斗的水流过大时,则会溢出去,溢出的水不会从漏斗口流出。

常见的限流算法-鸿蒙开发者社区

可以看出,漏桶算法的核心逻辑为:

  1. 缓存请求
  2. 匀速处理
  3. 多出的丢弃

漏桶算法会强行限制请求速率,这一特点导致漏桶算法有以下缺点:

不能应对突发流量

比如漏桶的流出速率为10QPS,容量为30,当来一波20次/s的请求,共维持10秒,那么到第3秒时,漏桶容量便已存满,这以后的请求都会被丢弃,直到漏桶有新的容量被释放。但是这种情况是很常见的,直接丢弃这种方式比较粗暴。

不能有效利用资源

因为漏桶的出口速率固定,假设设置为10QPS,那么当请求数量为12,15,10,8,5这种情况请求时,整体来看平均请求速率也是10QPS,但是在前三秒因为速率设定为10,则会直接丢弃7个请求。

令牌桶算法

令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。从名字上很多人会和漏桶算法混淆,但是它们有很大的差别。令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。

常见的限流算法-鸿蒙开发者社区

令牌桶算法的过程主要包含以下内容:

  1. 系统匀速的产生令牌存放到令牌桶中;
  2. 令牌桶的容量固定,当令牌桶填满后,再放入其中的令牌会被丢弃;
  3. 每个请求从令牌桶中获取令牌,如果获取成功则处理请求,如果失败则丢弃请求。

那么为什么说令牌桶算法可以应对突发请求呢?

首先我们来理解一下什么是突发请求。假如我们希望服务速率是100QPS,正常情况我们的请求不会超过100/s,如果某一秒请求到达150,这就是突发请求。

如果是漏桶算法,如果第一秒有80个请求,第二秒有150个请求,那么就会有50个请求被丢弃。

但是在令牌桶算法中我们设定每秒产生100个令牌,我们可以设定令牌桶的容量为120,那么第1秒80个请求会消耗80个令牌,到第一秒的150个请求到达时,可以处理120个请求,只丢弃30个。那么是否意味着只要桶的容量设置的足够大就都可以处理了呢?当然不是,因为超过120可能就已经远远超出了服务处理的能力,桶容量设置的超过服务能力也就丢失了限流的作用了。

RateLimiter

talk is cheap, show me the code!

在Guava中的RateLimiter便是使用令牌桶算法实现的。

常见的限流算法-鸿蒙开发者社区

运行结果:

常见的限流算法-鸿蒙开发者社区

从运行结果可以看到,很准确的保证了每秒最多只有三个线程获取到令牌,很好的对请求做了控制。

​Guava RateLimiter​​​的的​​acquire()​​方法默认是阻塞的,如果在获取不到令牌时会阻塞等待,知道获取成功。

​RateLimiter​​​除了​​acquire()​​方法外还有其他的使用方法。

  • tryAcquire():尝试获取1个令牌,如果失败则直接返回
  • tryAcquire(int permits):尝试获取多个令牌
  • tryAcquire(long timeout, TimeUnit unit):尝试获取一个令牌如果当前没有会等待指定时间,不会阻塞
  • tryAcquire(int permits,long timeout, TimeUnit unit):尝试获取多个令牌如果当前没有会等待指定时间,不会阻塞

需要特别注意的是,RateLimiter只能支持单机的限流,所以它无法针对集群做流量限制。

集群环境如果需要做流量限制,常见的方式是使用Redis,针对每秒的时间戳对请求数进行累加,超过限制数量则拒绝服务。

总结

本文主要介绍了两种常见的限流算法:漏桶算法令牌桶算法

漏桶算法因为强制限定请求速率,所以不能应对突发流量;

令牌桶算法能够一定程度的应对突发流量,具体能应对的阈值取决于服务的能力;

当然,在某些场景下如果需要严格保证请求速率,漏桶算法则比令牌桶更适合。

Guava RateLimiter使用令牌桶算法实现,可以做为单机限流方案的一种选择,集群环境下的限流方案可以选择Redis实现。


文章转载自公众号:小黑说java

分类
标签
已于2022-11-2 10:15:23修改
收藏
回复
举报
回复
    相关推荐