最近团队在搞流量安全控制,为了应对不断增大的流量安全风险。Waf防护能做一下接入端的拦截,但是实际流量会打到整个分布式系统的每一环:Nginx、API网关、RPC服务、MQ消息应用中心、数据库。瞬间的大流量对系统的整体的冲击还是很大的,一些连锁反应时刻刺激着我们的神经!所以,我们要设计开发我们自己的流控中心。解决我们的系统痛点问题。
一、首先,我们从系统需求说起:
1. 接入点IOT设备,瞬间批量上线&离线:几十万设备同时离线、上线后,带来的三遥数据、状态数据,瞬间打到后端。虽然有MQ扛了一层,但是MQ消费端是并发处理的,后端业务扛不住了
2. 大型促销发券活动,几十万用户瞬间异步发出上百万张优惠券。
3. API网关,时候接受第三方合作伙伴的定时访问,请求密集、数据访问大,后端RPC服务疲于应对。
4. DB层,上述场景最后60%的请求会落到DB端,磁盘、CPU压力大
5. Redis,大范围访问Redis中的缓存数据,TPS瞬间就上去了。
通过分析以上这些问题,都是由于瞬间流量过大造成,因此必须对大流量进行控制。流量控制越靠近链路开端,带来的效果越明显。
二、在设计流控之前,我们要看一下互联网业绩怎么做的!
我们借鉴了阿里的做法,毕竟作为云厂商和双11缔造者,流控方面的经验还是经过实际业务验证的!
总结一下:
- 流控说白了就是控制速率,在系统各个层面防止被大流量打爆
- 在系统的各个层面,流控策略和方式是不同的,API网关的、RPC服务的、MQ的
- 流量控制的维度可以是全局的、进程的、请求来源、用户及自定义维度
- 流量控制是动态的,应该根据系统的“并发特点”动态调配、控制
- 流控的根本目的就是保证系统的高可用性。
总结梳理了业界流量控制的做法之后,我们再看一下具体的流控算法及实现!
三、流控算法及实现
目前业界的流控算法的就这么几种:漏桶、令牌桶、计数器,我们一一展开介绍一下。
1. 漏桶(Leaky Bucket)算法
水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),
当水流入速度过大会直接溢出(访问频率超过接口响应速率), 然后就拒绝请求,
可以看出漏桶算法能强行限制数据的传输速率.
有两个变量:
- 一个是桶的大小,支持流量突发增多时可以存多少的水(burst),
- 另一个是水桶漏洞的大小(rate)。
漏桶算法的问题:
- 水满了直接不让进、丢弃
- 桶容量问题
- 漏出速度控制问题
2. 令牌桶算法(Token Bucket)
随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)
往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),
如果桶已经满了就不再加了. 新请求来临时,
会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务.
令牌添加速度支持动态变化,实时控制处理的速率.
令牌桶算法的问题:
- 令牌满了,请求无法处理
- 桶容量问题
- 令牌添加速度控制问题
3. 漏桶和令牌桶算法对比
- 令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求;
- 漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝;
- 令牌桶限制的是平均流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿3个令牌,4个令牌),并允许一定程度突发流量;
- 漏桶限制的是常量流出速率(即流出速率是一个固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2),从而平滑突发流入速率;
- 令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率;
- 两个算法实现可以一样,但是方向是相反的,对于相同的参数得到的限流效果是一样的。
4. 计数器
主要用来限制总并发数,比如数据库连接池、线程池、并发数;只要全局总请求数超过设定的阀值则进行限流,是简单粗暴的总数量限流,而不是平均速率限流。
- 限制总并发数(比如数据库连接池、线程池)
- 限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)
- 限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率)
- 限制远程接口调用速率
- 限制MQ的消费速率。
- 可以根据网络连接数、网络流量、CPU或内存负载等来限流
总结一下:
从流控算法上看,主要有三个核心要素:
- 可用容量
- 速率控制
- 容量打满后的处理策略:丢弃 or 阻塞
流控算法只是流量控制中核心的一环,并不是全部。实际应用上,需要结合实际的业务场景,采用适合的流控算法实现流量控制!
四、流控中心设计
通过上面需求分析、业界对标&对比、流控算法对比,我们规划了流控中心的产品特性,在此基础上完成一个V1.0版本的流控框架实现,同时准备开源出去,让更多的人可以直接使用。
管理时(设计时):
- 提供统一的流控策略定义,流控策略中可以指定使用具体控制方式,支持QPS、指定时间内访问量、并发数、延迟时间四种控制方式
- 支持对API网关服务、RPC服务、MQ等不同的主体,绑定不同的流控策略和阈值。
- 对于API网关服务,支持按服务、来源、用户、集群、进程设置QPS流量控制策略,同时支持按指定时间范围(分钟、小时、天)设置访问次数对应的流控策略。
- 对于RPC服务,支持按服务、集群、进程设置QPS流量控制策略,同时支持按指定时间范围(分钟、小时、天)设置访问次数对应的流控策略。
- 对于MQ,支持按队列、集群、进程设置消息消费的并发流量控制策略,同时支持延迟消费控制策略。
- 触发流控策略后,支持一种或者多种处理策略:请求失败、请求等待、预警等
- 流控策略和阈值支持动态调配,通过配置中心实时同步到各个流控模块,配置动态实时生效。
- 提供统一的流控日志查看功能,支持根据流控主体、时间实时查询流控触发情况。
运行时:
- 提供可独立运行的限流模块程序,Host在各类服务容器(API网关、RPC服务框架、MQ)中独立运行
- 运行时支持对QPS、指定时间内访问量、并发数、延迟时间四种流量控制方式,根据流控策略的定义实时控制流量。
- 提供对应用集群(全局)类的流控管理及应用进程(进程级)的流控管理
- 触发流控后,支持实时将监控、预警信息实时推送到监控预警平台
- 流控策略和阈值调整后,通过配置中心实时同步到各个限流模块程序,实时生效。
这是目前我们流控中心大致的规划和整理,其实技术原型代码我们已经写出来了,昨天完成UT通过了,下一篇博客我们将把这个流控框架一点点分享给大家。
提前剧透一下,我们借鉴了Git的RateLimiters
https://github.com/robertmircea/RateLimiters
周国庆
2018/5/16