字节三面的场景题
限流的目的是通过对于并发访问进行限速,一般是达到限制的速率,就会触发相应的限流行为。常见的限流行为如下:
- 拒绝服务。 把多出来的请求拒绝掉,受到流量暴增的时候,会统计当前的哪个客户端来的请求最多,直接进行拒绝,把带恶意的请求阻挡。
- 服务降级。关闭一些不太重要的服务,让给更重要的功能。另一种是返回部分数据。
- 特权请求。我们只把有限的资源分给重要的用户,我们应该把资源尽可能的分给特权用户。
- 延时队列。利用一个队列来进行缓冲大量的请求,如果队列满,则进行拒绝,一般用于应对短暂的峰刺请求。
限流的实现算法
计数器方式
最简单的算法是维护一个计数器,当请求来临的时候,做加一操作,当一个请求处理完后做减一操作。当计数器大于某个数,进行拒绝请求。
这种算法虽然实现简单,但是却有一个很大的缺陷:无法限制短时间之内的集中流量。假如我们需要限制每秒只能处理10个请求,如果该秒产生的请求都集中在最后10毫秒中,而下一秒钟的前10毫秒产生了10刺请求,那么在这20毫秒中产生了20次请求,因为这20 次请求分布在两个时间窗口内,所以没有触发限流。
计数器方式没有办法解决短时间的集中流量,所以我们在实际项目中使用其他的限流算法: 漏桶算法和令牌筒算法
漏桶算法与令牌筒算法
漏桶算法的示意图
漏桶算法的原理,就像流量会进入和暂存到漏桶里面,而漏桶的出口会按照一定的速率将流量漏出到接收端中。
如果流入的流量在某一段时间内大增,超过了漏桶的承受极限,那么多余的流量会触发限流策略,会被拒绝服务。
一般来说,该算法用一个队列进行实现,当请求过多的时候,队列开始积压请求,如果队列过满,那么会拒绝请求。比如说TCP,当请求的数量过多的时候会有一个 sync backlog 的队列来缓冲请求。
令牌桶算法
- 如果我们需要在一秒内限制访问次数为N次,那么就每隔 1 / N 的时间,往桶里放一个令牌;
- 在处理请求之前要先从桶中获得一个令牌,如果桶中没有令牌,那么需要等待新的令牌或者直接拒绝服务;
- 桶中的令牌总数也要有个限制,如果超过限制就不能向桶中再增加新的令牌了。这样可以限制令牌的总数,在一定程度上避免瞬时流量高峰的问题
总结:
限流是一种常见的服务保护策略,你可以在整体服务、单个服务、单个接口、单个 IP 或者单个用户等多个维度进行流量的控制;
1.计数器算法虽然能一定程度上实现限流的目的,但是都无法让流量变得更平滑;
2.令牌桶算法和漏桶算法则能够塑形流量,让流量更加平滑,但是令牌桶算法能够应对一定的突发流量,所以在实际项目中应用更多。