讲师中心 微信公众号
AI工具推荐 视频效率加速

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

酷明大大_6309

酷明大大_6309

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

|

438人浏览过

|

来源于php中文网

原创

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

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

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

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

用 ConcurrentQueue<datetime></datetime> 实现线程安全的滑动窗口

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

  • 必须用 DateTime.UtcNow(而非 DateTime.Now),避免时区和夏令时干扰
  • 出队操作不能只做一次——要 while 循环直到队首时间戳有效,否则会误判(例如多个请求在临界点涌入)
  • 队列长度本身不直接代表当前请求数,必须先清理过期项再取 Count(ConcurrentQueue 的 Count 是 O(n),高并发下慎用;改用原子计数器更稳)
public class SlidingWindowCounter
{
    private readonly ConcurrentQueue<DateTime> _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<datetime></datetime> 或 Redis

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

Windows 图片打印
Windows 图片打印

通过 Python 使用 mspaint /pt 在 Windows 上打印图像——绕过 bash 中文路径编码问题。

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

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

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

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

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

热门AI工具

更多
WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

讯飞智文

一款面向学习和办公场景的AI文档创作工具,可辅助生成PPT与Word文档,提高资料整理和内容制作效率。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

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

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

709

2024.05.11

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

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

644

2025.12.18

ThinkPHP中间件机制与请求拦截处理实践
ThinkPHP中间件机制与请求拦截处理实践

本专题围绕 ThinkPHP 中间件体系展开,深入讲解中间件的定义、注册与执行流程。内容包括全局中间件与路由中间件的区别、请求前后处理逻辑、自定义中间件开发以及权限验证与日志处理应用。通过实际案例,帮助开发者掌握中间件在项目中的核心作用与最佳实践。

458

2026.03.31

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

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

5110

2023.10.13

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

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

3748

2023.11.20

while的用法
while的用法

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

354

2023.09.25

curl_exec
curl_exec

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

3863

2023.06.14

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

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

915

2023.10.30

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

0

2026.11.06

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
phpEnv手册
phpEnv手册

共0课时 | 0人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn