概览
滑动窗口概念起源于网络通信,是一种流量控制机制。基于对指定窗口的指标统计,可以用于触发后续的处理措施。
例如分布式系统中,滑动窗口可以实现服务接口的限流。
本文针对滑动窗口在接口限流中的应用,分别提供了普通版(基于本地计数),分布式版(基于Redis计数)滑动窗口的实现。
算法思路
接口限流一般是指一定单位时间内,对于某个接口的请求量不允许超过某个阈值,如果超出阈值,则会拒绝请求。
算法思路包括如下核心点:
- 时间窗口的创建(窗口大小,窗口最小划分周期定义)
- 当前所属时间窗口定位,完成计数,统计操作
其中窗口最小划分周期可以理解为窗口每次移动的步长,也是统计计数的基本单位,这个步长的大小决定了窗口是否能平滑移动,步长定义越小,滑动窗口移动得越平滑,最终的统计结果也越精确。
如下,定义了一个滑动窗口:
- 窗口大小为 4s
- 窗口步长为 1s,即一个窗口被拆分成 4 块
假设每个窗口内的请求阈值定义为 10 个,可以看到:
- 窗口1的请求总数量为 8,没有达到阈值
- 窗口2的请求总数量为 14,触发了阈值限制,因此其中有 4 个请求会被拒绝
基本实现
代码实现思路如下。
定义滑动窗口属性:
滑动窗口实现:
统计当前窗口的请求数:
测试用例1:
熔断阈值设置为10,可以看到后10次请求都触发了熔断。
测试用例2:
熔断阈值设置为10,窗口大小为 10s,第11次请求触发熔断,之后休眠10s,过渡到下一个新的窗口,后面9次请求正常执行。
Redis 实现
基于Redis有序集合的特性,可以很简单就实现滑动窗口:
- 有序集合的 score 属性存储每个请求的时间戳
- 窗口数据统计请求量时,只需要把时间窗口外的元素舍弃,集合中剩余的元素数量之和便是结果
定义滑动窗口属性:
滑动窗口实现:
测试用例1:
熔断阈值设置为10,可以看到后10次请求都触发了熔断。
测试用例2:
熔断阈值设置为10,窗口大小为 10s,第11次请求触发熔断,之后休眠10s,过渡到下一个新的窗口,后面9次请求正常执行。
测试用例3(将窗口大小调整为20s,代码与测试用例2保持一致):
窗口大小设置为20s,第11个请求之后即使休眠了10s,后面9个请求仍然触发熔断,因为这些请求还在同一个窗口内。