Fix: overload_control/flow_control:smooth_limiter issues related to accuracy and efficiency #185 #186
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
解决方案
通过引入
RecentQueue
来实现滑动窗口算法。RecentQueue
用来保存滑动窗口内最近所有活跃请求的时间戳。从实现上说,依然使用 ring queue,该队列的初始大小为 QPS(滑动窗口内能够保存连接的最大值)。不难看出,由于
RecentQueue
保存时序,所以在RecentQueue
内时间戳螺旋递增,RecentQueue
是一个有序结构。在
RecentQueue
中cur
表示存放当前请求时间戳的位置。当新请求到达的时候,cur
指示RecentQueue
中缓存最久的时间戳。过滤请求
当一个请求到达的时候,该请求的时间戳为
now
。当
now - 1s < RecentQueue[cur]
时,说明当前RecentQueue
已满,且缓存最久时间戳RecentQueue[cur]
比较新,不能被淘汰,故应拒绝本次访问;当
now - 1s ≥ RecentQueue[cur]
时,说明缓存最久时间戳RecentQueue[cur]
缓存时间超过1s 可以被淘汰,故更新RecentQueue
。流程的时间复杂度为O(1)。
求当前时刻滑动窗口内活跃请求总数
由于
RecentQueue
具有螺旋递增的性质,其逻辑结构是一个有序结构,故能够通过二分查找找到第一个满足大于now - 1s
的位置,与当前 cur 做偏移量计算即可得到当前时刻滑动窗口内活跃请求总数。流程的时间复杂度为O(logn)。
算法设计思想
惰性求值
这一点沿用了之前设计令牌桶算法时的思想。
建模——以缓存设计的角度解决滑动窗口问题
熟悉缓存策略的朋友应该不难看出这个算法有点像
LRU
。RecentQueue
和Least Recently Used
稍有区别:RecentQueue
缓存时间戳只访问/缓存一次;Least Recently Used
缓存的对象可能被访问多次。访问次数的差异,造成了在底层数据结构选型的不同,LRU
需要频繁插入、删除,故而使用双向链表结构,另外,为了提升LRU
查找的能力,通常在LRU
实现的时候维护 hash 表。RecentQueue
有拒绝更新缓存的能力;相反,LRU
没有。算法优点