首页 游戏 软件 资讯 排行榜 专题
首页
编程语言
C++实现基于时间戳的限流算法 _ 令牌桶与漏桶原理实现【源码】

C++实现基于时间戳的限流算法 _ 令牌桶与漏桶原理实现【源码】

热心网友
53
转载
2026-05-06

C++实现基于时间戳的限流算法:令牌桶与漏桶原理实现【源码】

C++实现基于时间戳的限流算法 _ 令牌桶与漏桶原理实现【源码】

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

开门见山,先说结论:在C++服务端开发中,利用std::chrono配合原子变量,完全可以构建出线程安全且开销极低的令牌桶限流器。至于漏桶算法,在纯内存的服务端限流场景里,其实很少有必要去实现——它的核心是“恒定速率输出”,而服务端限流真正要防范的,往往是突发流量冲击。相比之下,令牌桶“允许突发+平滑限流”的特性,显然更贴合实际需求。

令牌桶:用 std::atomic 和 std::chrono::steady_clock 管理剩余令牌

实现高性能令牌桶,关键在于思路的转变:核心不是每秒去重置令牌数量,而是根据当前时刻,动态计算出“此刻应该有多少令牌”。这样一来,就彻底摆脱了对定时器或后台线程的依赖。具体怎么操作?有几个关键点需要把握:

  • capacity(桶容量)和rate_per_sec(填充速率)是基础配置,例如可以设为每秒填充100个令牌,桶最大容量为200个。
  • 存储的核心不是令牌数量本身,而是std::atomic类型的上一次填充时间戳(建议使用纳秒精度)。这个设计能大幅减少竞态条件。
  • 每次请求到来时,先计算出从上次填充到现在,理论上应增加的令牌数。公式是:(当前时间 - 上次填充时间) * 填充速率 / 10^9。然后,将这个数值与当前剩余令牌数取最小值,得到实际可用令牌。
  • 最后判断是否放行:如果可用令牌数 >= 本次请求消耗数,则通过原子操作更新剩余令牌值,并返回true。

来看一段示例代码的关键片段:

bool tryConsume(int tokens = 1) {
    auto now = std::chrono::steady_clock::now().time_since_epoch().count();
    auto& last = last_fill_time_;
    auto prev = last.load(std::memory_order_relaxed);
    long long a vail = 0;
    do {
        auto elapsed_ns = now - prev;
        double added = elapsed_ns * rate_per_sec_ / 1e9;
        a vail = std::min(static_cast(added), capacity_);
        if (a vail < tokens) return false;
    } while (!last.compare_exchange_weak(prev, now, std::memory_order_relaxed));
    // 此处需用 CAS 更新剩余令牌数(略去具体实现,推荐用带版本号的双原子变量或 mutex)
    return true;
}

想深入掌握?可以立即学习“C++免费学习笔记(深入)”。

漏桶不适合做接入层限流:它不解决“突发允许通过”的问题

为什么说漏桶在接入层限流中常常水土不服?根源在于它的设计哲学:强制以恒定速率输出请求。这意味着,即便桶是空的,新来的请求也只能排队等待。在HTTP接入层,这会直接放大请求延迟。更关键的是,它无法应对那些合理的、短时间的流量突增,比如秒杀活动的预热阶段。

实践中,漏桶常被误用,有几个典型的坑:

  • std::queue加一个定时器来模拟“漏水”,结果往往因为定时器精度差、线程调度抖动,导致实际漏速完全不可控。
  • 把漏桶简单当成“请求队列长度限制器”,但这本质上只是削峰填谷,并非严格意义上的限流。真正的限流,在超出能力时必须果断拒绝,而非无限制排队。
  • 试图混合使用令牌桶和漏桶,比如“令牌桶准入,漏桶排队”,这种设计不仅增加了系统复杂度,还可能带来额外的延迟,收益却微乎其微。

话说回来,漏桶就一无是处吗?当然不是。它真正的用武之地,在于那些对速率稳定性有硬性要求的场景,比如底层IO调度或硬件限速。在这些地方使用,也必须配合高精度时钟(例如CLOCK_MONOTONIC_RAW)甚至内核旁路技术,才有实际意义。

线程安全与性能陷阱:别用 std::mutex 锁整个桶

高并发场景下,如果用std::mutex把整个桶锁住,那性能基本就归零了,所有请求都会串行化。正确的做法是:

  • 所有读操作,比如时间计算、令牌估算,全部走无锁路径。只在最后真正扣减令牌的那一步,做最小粒度的CAS(比较并交换)操作。
  • 坚决避免使用std::time(nullptr)gettimeofday()这类可能发生时间回跳且精度不高的函数。必须使用std::chrono::steady_clock
  • 桶容量capacity不要设置得过大(比如10万)。过大的数值会导致浮点误差累积,建议控制在1000以内,并尽量使用整数运算来替代浮点运算。
  • 如果需要做分布式限流,那么本地的令牌桶只能作为最后一道防线。核心的限流逻辑必须下沉,采用Redis+Lua脚本(例如redis-cell模块)或者专用的限流服务来实现。

还有一个极易被忽略的设计要点:令牌桶对突发流量的容忍能力,其实取决于capacityrate_per_sec的比值。把配置设为100/100和1000/100,看上去平均速率都是100 QPS,但后者允许在某一秒内突发处理1000个请求——这个细微的设计权衡,文档里往往不提,可一旦线上流量出现波动,全靠它来兜底。

结论:C++中用std::chrono和原子变量可实现线程安全、低开销的令牌桶限流,而漏桶在纯内存服务端限流中基本不适用,因其无法应对突发流量,仅适合底层IO等对恒定速率有硬性要求的场景。
来源:https://www.php.cn/faq/2318171.html
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

c++如何解析MPEG-TS流中的PAT与PMT节目表【深度】
编程语言
c++如何解析MPEG-TS流中的PAT与PMT节目表【深度】

C++如何解析MPEG-TS流中的PAT与PMT节目表【深度】 PAT表是解析MPEG-TS流的关键起点,它固定位于PID为0x0000的TS包中。解析时需通过payload_unit_start_indicator标志定位新表起始,正确处理adaptation field以找到payload,校验

热心网友
05.06
C++ std::identity用法 _ 函数对象占位符与ranges算法【详解】
编程语言
C++ std::identity用法 _ 函数对象占位符与ranges算法【详解】

C++ std::identity用法详解:函数对象占位符与ranges算法核心指南 std::identity 核心概念与应用场景解析 在C++20标准库中,std::identity绝非简单的语法糖,而是std::ranges算法体系中表达“元素原样透传”意图的唯一标准函数对象。当你调用std:

热心网友
05.06
C++ std::is_base_of用法 _ 编译期检查类继承关系【干货】
编程语言
C++ std::is_base_of用法 _ 编译期检查类继承关系【干货】

std::is_base_of编译期报错解析:非法类型、不完整类型与非类类型传入的应对方案 std::is_base_of 编译期报错的根本原因 许多C++开发者在首次使用 std::is_base_of 模板时,常对其在编译阶段直接报错感到困惑。这源于其作为类型特征(type trait)的本质—

热心网友
05.06
c++如何读取和设置文件的扩展时间戳信息_出生时间提取【技巧】
编程语言
c++如何读取和设置文件的扩展时间戳信息_出生时间提取【技巧】

Linux下birth time仅能通过statx()读取且不可设置,需内核≥4 11、支持的文件系统及正确挂载选项;glibc未暴露该字段,stat()等传统接口无法获取。 Linux 下用 stat 和 utimensat 读取 设置 birth time(创建时间) 在Linux的世界里,文件

热心网友
05.06
c++ cista++序列化 c++如何进行极低延迟的对象序列化
编程语言
c++ cista++序列化 c++如何进行极低延迟的对象序列化

cista 实现微秒级序列化的核心原理:零开销内存拷贝与偏移重定位 cista 微秒级序列化的技术实现解析 cista 之所以能够实现微秒甚至纳秒级的序列化性能,源于其颠覆性的设计理念。与传统的序列化方案不同,cista 彻底摒弃了运行时类型识别(RTTI)、动态反射和堆内存分配等重型操作。它采用了

热心网友
05.06

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

荣耀400pro关机要按几秒
电脑教程
荣耀400pro关机要按几秒

荣耀400 Pro正确关机全指南:从常规操作到故障应对详解 需要关闭您的荣耀400 Pro手机?日常操作其实非常简便。只需长按位于机身右侧的电源键约3秒钟,屏幕上便会浮现一个简洁的半透明菜单,其中明确列出了“关机”、“重启”以及“紧急呼叫”选项。直接点击“关机”,系统将启动一次10秒的安全倒计时,随

热心网友
05.06
红米K30Pro如何拆后盖胶怎么清理
电脑教程
红米K30Pro如何拆后盖胶怎么清理

红米K30 Pro后盖拆解教程:专业工具与细致手法的完美结合 红米K30 Pro的后盖采用了高强度背胶配合隐藏式螺丝的双重固定设计,想要实现无损拆解,绝非依靠蛮力可以完成。整个操作流程对加热温度、撬启手法以及清洁标准都有严格要求,任何环节的疏忽都可能导致部件损伤。具体而言,其后盖边缘使用了耐高温的工

热心网友
05.06
三星zflip电池百分比需要root吗
电脑教程
三星zflip电池百分比需要root吗

无需Root权限:三星Galaxy Z Flip系列电量数字显示设置全解析 很多三星折叠屏手机用户都想知道,如何在状态栏直接查看精确的电池百分比数字,是否必须获取Root权限才能实现?实际上完全不需要。三星自Galaxy Z Flip 5、Z Flip 4等主流机型开始,已在系统层面内置了这一实用功

热心网友
05.06
笔记本开机自检时能看到DDR3或DDR4吗
电脑教程
笔记本开机自检时能看到DDR3或DDR4吗

笔记本开机自检信息虽不直接标注“DDR3”或“DDR4”,但联想、戴尔、华硕等品牌BIOS画面常以“PC3-”或“PC4-”编码间接揭示内存代际。UEFI自检显示的内存频率(如2400MHz 3200MHz)结合JEDEC规范可辅助推断:PC3对应DDR3,PC4对应DDR4。更高精度的识别方案包括

热心网友
05.06
空调制冷但不太凉是压缩机问题吗?
电脑教程
空调制冷但不太凉是压缩机问题吗?

空调制冷不足怎么办?先别急着维修压缩机,这些问题更常见 夏天开空调却感觉不够凉爽?很多朋友的第一反应是压缩机坏了,其实压缩机故障的概率相对较低。根据维修行业的大数据统计,绝大多数制冷效果不佳的情况,源于几个容易被忽略的日常维护与环境因素。滤网积尘、制冷剂泄漏、外机散热不良才是真正的高发原因。盲目更换

热心网友
05.06