0

0

c# 如何用 C# 实现一个滑动窗口计数器来做限流

星降

星降

发布时间:2026-02-09 09:52:02

|

378人浏览过

|

来源于php中文网

原创

滑动窗口计数器的核心逻辑是实时维护一个随请求滑动的时间窗口,每次请求时仅统计窗口内(如过去1秒)的有效请求时间戳,并动态清理过期数据;它依赖ConcurrentQueue线程安全地存储带时间戳的请求记录,通过while循环持续出队过期项后再判断队列长度是否超限,避免误判与性能陷阱。

c# 如何用 c# 实现一个滑动窗口计数器来做限流

滑动窗口计数器的核心逻辑是什么

滑动窗口计数器不是简单地把时间切分成固定桶(如每秒一个桶),而是让窗口随请求实时滑动,比如“过去 1 秒内最多 100 次请求”。关键在于:每次请求到来时,只统计那些时间戳 ≥ DateTime.UtcNow.AddSeconds(-1) 的记录,丢弃过期数据。它比固定窗口更平滑,但需要维护带时间戳的请求历史——不能只用一个整数累加。

ConcurrentQueue 实现线程安全的滑动窗口

这是最轻量、无外部依赖的做法,适合中低并发(QPS ≤ 数千)。ConcurrentQueue 天然线程安全,避免锁开销;每次请求入队,再循环出队过期时间戳,最后检查队列长度是否超限。

  • 必须用 DateTime.UtcNow(而非 DateTime.Now),避免时区和夏令时干扰
  • 出队操作不能只做一次——要 while 循环直到队首时间戳有效,否则会误判(例如多个请求在临界点涌入)
  • 队列长度本身不直接代表当前请求数,必须先清理过期项再取 CountConcurrentQueueCount 是 O(n),高并发下慎用;改用原子计数器更稳)
public class SlidingWindowCounter
{
    private readonly ConcurrentQueue _timestamps = new();
    private readonly int _windowSeconds;
    private readonly int _maxRequests;
    private readonly object _cleanupLock = new(); // 避免 Count 被频繁调用时反复遍历

    public SlidingWindowCounter(int windowSeconds, int maxRequests)
    {
        _windowSeconds = windowSeconds;
        _maxRequests = maxRequests;
    }

    public bool TryAcquire()
    {
        var now = DateTime.UtcNow;
        var windowStart = now.AddSeconds(-_windowSeconds);

        // 清理过期时间戳:必须 while 循环,不能只 pop 一次
        while (_timestamps.TryPeek(out var ts) && ts < windowStart)
        {
            _timestamps.TryDequeue(out _);
        }

        // 使用锁保护 Count 访问(或改用 Interlocked 原子计数器)
        lock (_cleanupLock)
        {
            if (_timestamps.Count >= _maxRequests) return false;
            _timestamps.Enqueue(now);
        }
        return true;
    }
}

为什么不用 SortedSet 或 Redis

SortedSet 看似能快速范围查询,但它不支持按时间范围批量删除(RemoveWhere 是 O(n) 且非线程安全),实际性能不如队列 + 首尾扫描;而 Redis 的 ZREMRANGEBYSCORE + ZCARD 虽标准,但引入网络延迟和序列化开销,在单机高吞吐场景下反而成瓶颈。纯内存方案只要控制好队列大小(比如加个最大容量限制防内存泄漏),对大多数 Web API 限流已足够可靠。

OFFER快
OFFER快

首个全流程托管的 AI 求职 Agent(自动筛选、沟通、网申)

下载
  • 没做最大容量限制?极端情况下(长时间无请求后突发洪峰),队列可能积压数万条时间戳,导致清理变慢甚至 GC 压力上升
  • 没考虑系统时钟回拨?如果 NTP 同步导致 UtcNow 突然变小,会误删大量未过期记录——生产环境建议搭配单调时钟(如 Stopwatch.GetTimestamp() 换算)
  • 这个类不是完全无锁lock 块只保护 CountEnqueue,但清理逻辑本身是无锁的;若追求极致性能,可改用 Interlocked 维护计数器,把时间戳存进 ConcurrentBag 并定期批量清理(需额外调度)

如何在 ASP.NET Core 中集成并验证效果

别直接在 Controller 里 new 实例——每个请求新建一个计数器就完全失效了。必须注册为 Singleton,并通过依赖注入使用。同时,限流结果要明确返回 HTTP 429,不能静默失败。

  • 注册服务:services.AddSingleton(sp => new SlidingWindowCounter(1, 100));
  • 在中间件或 ActionFilter 中调用 TryAcquire(),失败时写入 context.Response.StatusCode = 429 并设置 Retry-After: 1
  • 验证时用 curl -X GET http://localhost:5000/api/test & 快速并发 200 次,观察响应头和状态码分布;注意不要用 Postman 自带的“发送多次”功能——它默认串行,测不出并发效果

真正难的是边界场景:窗口跨越秒级边界时的计数抖动、多实例部署时的共享状态缺失、以及和熔断/降级策略的协同。单机滑动窗口只是起点,不是终点。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
什么是中间件
什么是中间件

中间件是一种软件组件,充当不兼容组件之间的桥梁,提供额外服务,例如集成异构系统、提供常用服务、提高应用程序性能,以及简化应用程序开发。想了解更多中间件的相关内容,可以阅读本专题下面的文章。

180

2024.05.11

Golang 中间件开发与微服务架构
Golang 中间件开发与微服务架构

本专题系统讲解 Golang 在微服务架构中的中间件开发,包括日志处理、限流与熔断、认证与授权、服务监控、API 网关设计等常见中间件功能的实现。通过实战项目,帮助开发者理解如何使用 Go 编写高效、可扩展的中间件组件,并在微服务环境中进行灵活部署与管理。

222

2025.12.18

软件测试常用工具
软件测试常用工具

软件测试常用工具有Selenium、JUnit、Appium、JMeter、LoadRunner、Postman、TestNG、LoadUI、SoapUI、Cucumber和Robot Framework等等。测试人员可以根据具体的测试需求和技术栈选择适合的工具,提高测试效率和准确性 。

447

2023.10.13

counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

198

2023.11.20

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

100

2023.09.25

curl_exec
curl_exec

curl_exec函数是PHP cURL函数列表中的一种,它的功能是执行一个cURL会话。给大家总结了一下php curl_exec函数的一些用法实例,这个函数应该在初始化一个cURL会话并且全部的选项都被设置后被调用。他的返回值成功时返回TRUE, 或者在失败时返回FALSE。

450

2023.06.14

linux常见下载安装工具
linux常见下载安装工具

linux常见下载安装工具有APT、YUM、DNF、Snapcraft、Flatpak、AppImage、Wget、Curl等。想了解更多linux常见下载安装工具相关内容,可以阅读本专题下面的文章。

179

2023.10.30

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

613

2023.08.10

Golang处理数据库错误教程合集
Golang处理数据库错误教程合集

本专题整合了Golang数据库错误处理方法、技巧、管理策略相关内容,阅读专题下面的文章了解更多详细内容。

98

2026.02.06

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
进程与SOCKET
进程与SOCKET

共6课时 | 0.4万人学习

Redis+MySQL数据库面试教程
Redis+MySQL数据库面试教程

共72课时 | 6.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号