系统设计之限流

系统设计之限流

限流

限流: 通过限制到达系统的并发请求数量,保证系统能正常响应部分用户请求,对于超出限制的流量拒绝服务保证整体的系统可用性。

  • 入口,例如API 网关层
  • RPC客户端

容器端限流

tomcat

连接池maxThreads

Nginx

  • 速率控制
  • 并发数

limit_req_zone 用来限制单位时间内的请求数,即速率限制

limit_conn_zonelimit_conn控制并发数,

服务端限流

滑动窗口

固定窗口: 1秒限制1万次, 只需要比较counter 是否大于阈值,需要timer 重置计数器。但是缺陷明显: Screen Shot 2020-09-12 at 2.15.14 PM

滑动窗口: 时间窗口细分。

Redis zest 实现

private static boolean isPeriodLimiting(String key, int period, int maxCount) {
long nowTs = System.currentTimeMillis(); // 当前时间戳
// 删除非时间段内的请求数据(清除老访问数据,比如 period=60 时,标识清除 60s 以前的请求记录)
jedis.zremrangeByScore(key, 0, nowTs - period * 1000);
long currCount = jedis.zcard(key); // 当前请求次数
if (currCount >= maxCount) {
// 超过最大请求次数,执行限流
return false;
}
// 未达到最大请求数,正常执行业务
jedis.zadd(key, nowTs, "" + nowTs); // 请求记录 +1
return true;
}

漏桶算法

类似于生活中的漏斗,无论上面的水流倒入漏斗有多大,也就是无论请求有多少,它都是以均匀的速度慢慢流出的。当上面的水流速度大于下面的流出速度时,漏斗会慢慢变满,当漏斗满了之后就会丢弃新来的请求;当上面的水流速度小于下面流出的速度的话,漏斗永远不会被装满,并且可以一直流出。

具体思想: 通过队列保存请求,队列满了后就放弃新请求,线程定期从队列获取一个或多个任务。

nginx 就是使用漏桶算法。

令牌算法

Screen Shot 2020-09-12 at 2.30.12 PM

恒定速率产生令牌,并存入令牌桶,每个请求必须先获取令牌才能执行。

如果我们需要在一秒内限制访问次数为 N 次,那么就每隔 1/N 的时间,往桶内放入一个令牌;

在处理请求之前先要从桶中获得一个令牌,如果桶中已经没有了令牌,那么就需要等待新的令牌或者直接拒绝服务;

桶中的令牌总数也要有一个限制,如果超过了限制就不能向桶中再增加新的令牌了。这样可以限制令牌的总数,一定程度上可以避免瞬时流量高峰的问题。

guava RateLimiter.

漏桶和令牌桶算法最明显的区别就是是否允许突发流量(burst)的处理,漏桶算法能够强行限制数据的实时传输(处理)速率,对突发流量不做额外处理;而令牌桶算法能够在限制数据的平均传输速率的同时允许某种程度的突发传输。

总结

  • 限流是一种常见的服务保护策略,你可以在整体服务、单个服务、单个接口、单个 IP 或者单个用户等多个维度进行流量的控制;
  • 基于时间窗口维度的算法有固定窗口算法和滑动窗口算法,两者虽然能一定程度上实现限流的目的,但是都无法让流量变得更平滑;
  • 令牌桶算法和漏桶算法则能够塑形流量,让流量更加平滑,但是令牌桶算法能够应对一定的突发流量,所以在实际项目中应用更多。

其他

Alibaba Sentinel 限流、熔断 https://mp.weixin.qq.com/s/BfbdhGZ9CZdvDdJGTYMjTA


https://mp.weixin.qq.com/s/chKUBzAPMbJY3nlYDyKFfQ

https://mp.weixin.qq.com/s/rj--eRAnQzg2p29NUrYqQQ

https://mp.weixin.qq.com/s/Ya_aw4_4s8UdZ-y9YjMiFw

https://mp.weixin.qq.com/s/1Mb0Wk0VmXca7ce3CRulAg