我们在做任何系统设计或者解决大部分疑难杂症的问题,都逃不开性能+容量这两个问题。而性能的问题大多数是日常每时每刻都可以反应出来系统设计的好坏与工程师的水平,但是容量问题就像是地震或者海啸,平日里不会出问题,一旦发生就是突袭的灾难,没有预案或者提前防护,基本就是等死的状态。
所以流控体系是一个复杂且重要,并且不同的系统基本没有特别通用技巧,这个领域值得深入来探讨一下~
这一期向集团内也是公司内非常资深的流控技术大佬---见深邀稿,高可用三板斧“缓存限流熔断”里限流的部分。内容深度原创并且实际的场景也是业界绝无仅有的真实案例,俗话说得好,没有那么多个惊心动魄、汗流浃背的日夜,也就没有那么多深入骨髓的理论经验总结。
本文篇幅较长,会涉及端到端流控的一下知识与经验
*算法原理
*客户端流控
*接入层流控
*应用层流控
*流控多维度配置选择
*单机/集群流控选型
*如何体系化评估当前系统的流控水位
希望看完,你也可以成为半个限流专家!
欢迎关注微信公众号「小爱同学的企服技术笔记」,获取完整细节文章。
单机&分布式限流的一些基本知识
咱们由浅入深,先来学习一些基本的限流算法与限流器设计原理。
**常见的流量控制算法:
**
*代币桶算法(Token Bucket)&漏桶队列算法(Leaking Bucket)
这种都常见于简单的单机限流器,满足单维度(时间、IP、组合维度等)的计数请求后,就会拒绝访问。
代币桶算法(Token Bucket)
漏桶队列算法(Leaking Bucket),会进行队列暂存和时间窗口匀速处理
*基于单位时间的滑动窗口系列算法
1、固定窗口计数器算法(Fixed Window Counter)。
【固定的】时间窗口内,[1:00:00, 1:00:01]进行计数统计,超越则丢弃请求,容易在边界时间误伤
2、滑动窗口算法(Sliding Window Log)
以上图为例,限流2qpm(query per min),严格基于当前时间-120s统计请求数,每一个需要回溯最早一个请求的具体时间,需要一定的细节存储和计算量,对于系统的侵占性较高,优秀的系统应该把更多的资源让给业务逻辑处理。
基于时间占比统计的滑动窗口,只需要知道已经过去的时间块内的统计数量即可,并不做严格的具体时间计算,减小限速器对整体系统的侵占性。
以图例,限制5qpm,前70%时间通过了5个请求,应该被限速。但是随着时间的推移,这5个请求带来的权重影响会减小,5*0.7=3.5 -> 4*0.6=2.4,当往后挪动时,当前分钟再来4个请求 4*0.6+4*0.4=4 < 5,仍是允许请求通过的,但是实际一分钟时间块内已经承载了8次请求。
这种滑动窗口的限速算法,不依赖存储进行计算,内存利用高效,近似进行估计。保证了计数的相对精准,时间窗口不会有特别大的误差,也最大化程度简化了系统自身的回溯查询和自然消耗,是一个较好的平衡点。
精确的时间窗口和按照权重计算的时间窗口,本质是对“硬”流量和“软”流量的区别策略。系统运行期大部分情况软流量计算时间窗口内,认为是均匀分布的即可,所以后者更适合大多数系统。
了解了基本的限流核心算法,我们来切入正题,
端到端流控分层
主流的互联网应用架构基本流量都会做三层调度分发,流控体系也应该从这三层“漏斗”内做体系化治理
定位 | 核心能力 | |
客户端流控 | 保护服务端 | 本地接口限流,端到端退避 |
接入层流控 | 保护自身和下游 | 单设备+接口限流,端到端退避(与客户端配合) |
应用层流控 | 保护自身和下游 | 所有入口多维度限流,端到端退避(与客户端配合) |
客户端流控
客户端的典型特点就是为了用户的流畅、不阻塞操作会大量采用异步处理,单端单个接口可以轻松发起几十万的qpm的请求,客户端同时在线的端也非常多(很多大型的app在线量会有逼近亿级) ,即使是每个客户端1个 qps也能产生千万级别的流量,服务端有限流能力也扛不住这么大的流量(限流的本质是基本的计数能力),所以客户端需要有限流能力以保护服务端。
客户端限流的核心:本地单接口限流, 端到端退避
因为Mac、Android、Ios、Win同账号之间的不同端对于其他端的请求是不感知的,所以只能服务端来进行感知请求量(计数),并且进行拦截。所以客户端限流是需要有服务端的中心化统计和反馈机制给到端的。
1、正常情况下的客户端限流
通过客户端的配置实现对任意接口的限流,比如用户user_id级别、api级别、api+payload(请求自定义key)级别的限流,限流值可以通过客户端预埋,也可以通过限流配置中心推送下发等等。
2、触发服务端限流情况下的客户端限流
服务端触发限流后,会直接向客户端返回状态码比如429(Too Many Requests)的响应,该响应中包含限速或者限流的值,客户端收到该响应后会自动基于其中的限流值限制相同api的请求。
3、端到端退避保护机制
核心逻辑是服务端限流后返回一个退避时间,在该时间内客户端不再访问该接口,避免大量客户端一起将服务端压垮。
接入层流控
客户端限流能力其实是需要不断建设的,也是整个流控体系里比较基础和薄弱的一环。即使不断的迭代新版本端有限流能力,老版本端也会长期存在,而且客户端可能会被hack,所以客户端的异常流量是会一直存在。所以接入层作为整个系统应用的入口,其限流能力也是非常必要的,一方面可以保护网关接入层自身,一方面可以保护后端的应用。
比较健壮的接入层支持如下几种限流能力:
1、单设备+单url限流能力
单个客户端常见的做法是一段时间内连在单台接入层机器上(长链接),利用这个特性,接入层通过单机限流的方式,以非常低的成本提供“****用户+接口粒度”的热点限流能力。这区别于随机选择接入层机器的做法,如果接入层的机器不固定,那么就需要使用中心化的集群限流能力,各大云厂商都提供专门的分布式限流服务,那么就需要额外为这一块进行成本预算。
2、单机自适应限流能力
基于诺亚(Noah)实现了自适应限流,消除容器实例容量不一致引起的问题。完全根据自身负载进行无差别限流容易造成误伤,为此,我们会优先保障大客户流量。
Noah:blog.csdn.net/Taobaojishu…
3、额外的分布式集群检测+限流能力,例如阿里云霸下7层流量清洗
数据防爬:防止数据被恶意爬取,如用户信息获取等接口
拒绝服务:可根据uid、ip、接口、ua 等维度,配置规则对用户操作进行阻断
流量洪峰防护:支持单机限流
流量访问控制:基于请求基础要素如 devid、uid、ip、ua 等维度设定相应的执行策略
4、其他限流维度
用户维度限流:单位时间内单用户允许通过的总流量
流量访问控制:基于用户、ip、接口等维度的访问控制(X5 成本问题补充)
欢迎关注微信公众号「小爱同学的企服技术笔记」,获取完整细节文章。
**总结****
讲述了这么多的限流细节,相信你一定有了许多的收获,当然我们也基于实践更向上一层,提供了一些非客观标准的限流成熟度模型,可以给读者一些参考,看看自己的系统在"海啸"面前,构筑的高并发、高可用堤坝到了哪一种水准~
限流的成熟度模型
等级 | 接口限流 | 接口+来源限流 | 接口+热点限流 | 扩散后限流 | 客户端限流&退避 | 其他特性 | 备注 |
L0 | 无 | 无 | 无 | 无 | 无 | 1、这里的接口包含了RPC的接口和MQ 的topic。 2、高危场景 一般是有故障点的接口、大流量的接口、大扩散的接口 | |
L1 | 高危场景开启 | 无 | 有 | 无 | 无 | 定制的验证逻辑 | |
L2 | 高危场景开启 | 高危场景开启 | 高危场景开启 | 无 | 无 | ||
L3(达标) | 所有场景开启 | 所有场景开启 | 所有场景开启 | 高危场景开启 | 无 | 通用手动验证 | |
L4 | 所有场景开启 | 所有场景开启 | 所有场景开启 | 需要场景开启 | 无 | 预警能力、突袭演练平台、流控数据报告(包括缺失信息) | |
L5 | 所有场景开启 | 所有场景开启 | 所有场景开启 | 需要场景开启 | 有 | 常态自动化演练和报告 |
1、基本要求:核心应用 达到L3(参见下面), 即开启接口维度限流、按照来源限流、按热点限流
2、高阶要求:核心应用达到L4,即在L3基础上,高危场景开启扩散后限流,开启端到端退避
到这里,相信你一定对于端到端流控体系的算法原理、客户端流控、接入层流控、应用层流控、流控多维度配置选择、单机/集群流控选型以及如何评估当前系统的流控水位等等有了一定的理论经验技巧。
如果你也有很多端到端流控的经验与思路,欢迎评论区留言给我们交流~