Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: overload_control/flow_control:smooth_limiter issues related to accuracy and efficiency #185 #186

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

AntiBargu
Copy link
Contributor

解决方案

通过引入 RecentQueue 来实现滑动窗口算法。

RecentQueue 用来保存滑动窗口内最近所有活跃请求的时间戳。从实现上说,依然使用 ring queue,该队列的初始大小为 QPS(滑动窗口内能够保存连接的最大值)。

不难看出,由于 RecentQueue 保存时序,所以在 RecentQueue 内时间戳螺旋递增,RecentQueue 是一个有序结构。

RecentQueuecur 表示存放当前请求时间戳的位置。当新请求到达的时候,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)。

调用时机:或许只有开启 report 后才会调用。

算法设计思想

  • 惰性求值

    这一点沿用了之前设计令牌桶算法时的思想。

  • 建模——以缓存设计的角度解决滑动窗口问题

    熟悉缓存策略的朋友应该不难看出这个算法有点像 LRURecentQueueLeast Recently Used 稍有区别:

    • RecentQueue 缓存时间戳只访问/缓存一次; Least Recently Used 缓存的对象可能被访问多次。访问次数的差异,造成了在底层数据结构选型的不同,LRU 需要频繁插入、删除,故而使用双向链表结构,另外,为了提升 LRU 查找的能力,通常在 LRU 实现的时候维护 hash 表。
    • RecentQueue 有拒绝更新缓存的能力;相反,LRU 没有。

算法优点

  • 不引入额外的定时器
  • 不以帧为单位的累加计数器求和的方式计算当前活跃连接数
  • 纳秒级精度,误差忽略不计
  • 算法效率高

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant