尧图精选

LLM应用限流实战:令牌桶、滑动窗口与配额管理

🕒 发布时间:2026/9/6 6:22:28 📁 来源:尧图网络
做了快一年 LLM 应用的网关和 BFF 层我越来越确定一件事在 LLM 应用里Rate Limit 不是一个“要不要做”的功能而是一个“做不好就出事故”的工程底线。传统 API 的限流最多是保护后端不被打挂但到了 LLM 这里限流同时要管住成本、管住上游配额、管住多租户公平性任何一个维度失控账面上都是真金白银的损失。这篇文章我想把生产环境里用到的限流方案完整拆一遍令牌桶、滑动窗口、API 配额管理以及它们该怎么组合、参数怎么定、上线后踩过哪些坑。内容偏工程实践适合正在做 LLM 应用网关、Agent 平台或者 API 转发层的同学参考也适合刚接触限流、想系统理解几种算法差异的读者。我会把每一步的“为什么”也讲清楚不只是甩一段 LUA 脚本就完事。1. LLM 应用为什么绕不开限流这个问题1.1 成本失控是第一个要面对的问题先讲一个最直接的痛点成本。传统 REST API 的单次调用成本基本是固定的限流主要为了“保护系统”。但 LLM API 的成本和 Token 强绑定单次请求的消耗可能差出两个数量级。一个用户问“帮我总结这份合同”Prompt 里塞了 8000 Token 的上下文加上输出预期 2000 Token一次请求就是 1 万 Token。另一个用户问“今天天气怎么样”可能 200 Token 就结束了。同样是调用一次接口成本差了 50 倍。如果你只用“每分钟请求数”这种维度去控根本控不住钱。更麻烦的是 Agent 场景。一个复杂任务可能会触发十几轮模型调用而且每一轮的输入都带着前面所有轮的上下文Token 消耗是指数级的。我就见过线上一个 Agent 循环因为某次工具返回异常在一个小时里重试了上千次直接把当天的预算打穿。事后追查故障的根源不只是重试逻辑写得烂——网关层根本没有一个像样的速率控制和配额拦截所有请求都是“裸奔”进上游的。所以我在 LLM 网关里做的第一件事就是把“按请求次数限流”升级成“按 Token 口径限流”并且把限流和预算、配额挂在一起。这已经不是性能优化而是财务控制。1.2 上游供应商的硬限制决定了你的天花板第二个原因是上游 API 本身有配额。OpenAI、Anthropic、Google 这些厂商都会对每个 API Key 设置 RPM每分钟请求数和 TPM每分钟 Token 数部分还会限制并发连接数。不同账号等级对应的配额差别很大同一个 Key 在不同模型上的 TPM 也可能不一样。这些限制是硬性的。一旦超过上游直接返回 429并且响应头里带一个 Retry-After 告诉你多久之后再试。如果你的应用没有自己的限流层而是依赖上游的 429 来被动刹停会出现两个很糟糕的连锁反应第一请求已经发出去了该花的钱已经花了但调用失败了等于白白浪费配额和预算。第二客户端收到 429 后如果重试逻辑写得不讲究比如立即重试、多线程并发重试会在上游形成“重试风暴”把本来只是轻微超限的情况放大成持续不可用。所以我一直跟团队强调上游配额是你的“物理上限”你自己的限流必须在这个上限之下再加一道闸给自己留出至少 20% 到 30% 的冗余。否则你根本没法控制什么时候触发上游的 429所有高可用设计都无从谈起。1.3 限流在整个 LLM 应用架构里的位置从架构视角看限流不应该散落在业务代码里而应该集中在网关层统一治理。我们的简化结构是这样客户端请求进入 API 网关网关先做身份认证、API Key 解析、模型路由然后进入限流模块。限流模块同时做三件事一是按请求速率做短期控制令牌桶、滑动窗口防止某秒钟的突发把上游打爆二是按配额做中长期控制小时、天、月维度的 Token 总额防止预算超支三是把多租户的用量分开记账避免一个用户把全平台的配额吃掉。这三件事不是一回事也不能混在一个计数器里实现。我在下面几节分别展开讲算法、实现和生产细节。2. 令牌桶算法先把最经典的方案吃透2.1 令牌桶到底在做什么令牌桶是限流算法里的“万金油”。它的思想很直白有一个桶容量固定里面装着令牌令牌以一个固定的速率持续注入每个请求进来必须先取走若干令牌取不到就拒绝或排队。这个算法有两个关键参数桶容量burst capacity和注入速率refill rate。注入速率决定了长期平均速率桶容量决定了允许的瞬时突发量。我习惯用一个类比来解释想象一个水龙头往杯子里滴水杯子满了就溢出去令牌丢弃每个请求来的时候要喝一口水。如果你一分钟只喝一次即便杯子很小也完全没问题但如果你想一口气连喝十次杯子就得有十口水那么多。杯子大小控制的是你“最多能多急”滴水速度控制的是你“长期能喝多少”。对应到 LLM 场景这个特性非常重要。因为 LLM 应用的流量天然带着突发性早高峰、活动引流、某个 Agent 任务突然并发都可能在一两秒内涌进来一大批请求。令牌桶允许这波突发正常通过同时又能保证在分钟级维度上不超过上游 TPM 配额。2.2 用 Redis Lua 实现一个生产可用的令牌桶先说不推荐的做法用简单的GETSET来实现令牌桶。你读一下当前令牌数扣减再写回去两步之间有并发窗口在高并发下会超发限流就等于摆设。正确做法是用 Redis 的 Lua 脚本把“读取剩余令牌、计算补充、判断是否发放、扣减”这整串操作做成原子的。脚本本身有不错的通用性-- KEYS[1]: 令牌桶的 key -- ARGV[1]: 每秒补充速率小数 -- ARGV[2]: 桶容量 -- ARGV[3]: 当前时间秒级时间戳由应用传入 -- ARGV[4]: 本次请求消耗的令牌数Token 估算值 local key KEYS[1] local rate tonumber(ARGV[1]) local capacity tonumber(ARGV[2]) local now tonumber(ARGV[3]) local requested tonumber(ARGV[4]) local current tonumber(redis.call(get, key)) if not current then current capacity end local last_refill tonumber(redis.call(get, key .. :ts)) if not last_refill then last_refill now end local elapsed math.max(0, now - last_refill) current math.min(capacity, current elapsed * rate) if current requested then redis.call(set, key, current - requested) redis.call(set, key .. :ts, now) redis.call(expire, key, 86400) redis.call(expire, key .. :ts, 86400) return 1 else return 0 end有几个细节我必须专门说明。第一为什么把时间戳放在单独的 key 里而不是和令牌数放在同一个 value我这么做是为了避免每次补充都要对一个复合结构做序列化反序列化也方便后续单独检查时间戳。当然你也可以把两个值打包成 hash 或者 JSON但实测下来单独的字符串 key 最简单、性能也最好。第二为什么时间戳由应用传入而不是在脚本里调redis.call(TIME)因为 Redis 脚本要保证在复制和 AOF 持久化环境下的确定性。脚本里调用 TIME 会拿到不确定的值主从切换之后可能产生数据不一致。生产上稳妥的做法是把now作为参数传进去时钟精度的损失可以接受。第三TTL 一定要设置。如果不设 TTL每个 API Key、每个模型、每个维度都会留下永久 key跑几个月 Redis 内存会涨到让你怀疑人生。我通常把限流 key 的 TTL 设成一天但要注意每次请求都会刷新 TTL所以那些“不活跃的 key”会在一天内自动清理活跃 key 则持续保留逻辑上没有问题。调用这段 Lua 脚本非常轻。我们用 Spring Boot 的DefaultRedisScript封装后单次限流判断和扣减的 RTT 在局域网环境下大概是 0.1 到 0.3 毫秒P99 也不会超过 1 毫秒完全够网关层用。2.3 令牌桶的参数应该怎么定参数定不好算法再完美也白搭。我把自己的计算思路分享出来。假设上游某模型的 TPM 配额是 10 万 Token/分钟。我们不想打满留 30% 余量那么可用速率就是 7 万 Token/分钟折合每秒约 1167 Token。这就是rate参数。桶容量capacity怎么定它等于允许的最大突发量。我会先算一个业务上可接受的突发窗口比如允许一次性突发 3 秒的配额量那就是 1167 × 3 ≈ 3500 Token。再把单次请求的平均 Token 消耗算进去假设平均每个请求 1200 Token那么 3500 Token 大约允许 2 到 3 个请求瞬间同时冲过去。这个参数不是定一次就完事了。我建议把rate和capacity放到配置中心线上通过监控数据持续校准。如果发现某个时段频繁触发 429 但上游其实还很宽裕说明余量留大了可以把余量从 30% 调到 20%。反过来如果经常打到上游 429说明余量不够赶紧调低。还有一点必须在参数层面考虑Token 估算误差。网关在请求发出前只能拿到 Prompt 的长度和max_tokens真正消耗的 Token 只有在响应返回后才知道。为了不超发我习惯在令牌桶这一层用“估算值 固定系数”来扣减系数取 1.1 左右给估算误差留余量。误差的最终修正放在配额记账那层处理后文会讲。3. 滑动窗口从毛刺到平滑3.1 固定窗口的临界突刺问题如果只用一个“每分钟重置”的计数器来做限流会遇到一个很经典的临界突刺问题。假设你定的是每分钟 60 次请求。某个整分钟窗口的第 59 秒已经进来了 60 次请求全部通过下一秒窗口重置计数器清零又有 60 次请求在一个瞬间涌进来。也就是说在 59 秒和 60 秒的交界处你在两秒内放过了 120 次请求是设定上限的两倍。放在 LLM 场景里这种两倍突刺是非常危险的。上游对你的 TPM 限制同样按分钟计算但人家有熔断和防护机制你这边连续两个窗口打满甚至超发上游极有可能直接把你当成恶意流量处理甚至临时封禁 Key。我们曾经就因为这个原因被某家供应商封了整整十分钟的调用权限线上所有依赖该模型的功能全部降级。固定窗口的问题本质在于它能控制每个独立的分钟窗口却控制不了跨越窗口边界那一段的流量密度。解决办法就是滑动窗口。3.2 两种滑动窗口实现方式对比业界常见的滑动窗口实现有两种滑动窗口日志Sliding Window Log和滑动窗口计数Sliding Window Counter。滑动窗口日志的核心是用一个有序集合记录窗口内每一次请求的时间戳。新请求进来时先把窗口外的旧时间戳删掉然后统计集合大小达到上限就拒绝。这个方案精度最高是真正意义上的“任意一秒窗口内都不超限”。代价是内存占用和窗口内的请求数成正比高 QPS 场景下 Redis 里会堆大量时间戳成员。滑动窗口计数则是把大窗口切分成若干小格子比如把 60 秒切成 6 个 10 秒的格子每个格子单独计数。判断当前请求是否允许时把所有落在当前窗口内的格子加起来。这样做内存占用和格子数量相关和请求量无关但精度有损失——窗口边界附近会有最多一个格子宽度的误差。我在网关里用的是什么矩阵分场景混用。对上游 TPM 这种“一旦超了就会被封”的硬限制我用滑动窗口日志宁可多占点 Redis 内存也要保证精度。对内部业务侧的每分钟请求数限制我用滑动窗口计数省内存、够用。滑动窗口日志的 Lua 脚本如下-- KEYS[1]: 滑动窗口 key -- ARGV[1]: 当前时间毫秒级时间戳 -- ARGV[2]: 窗口大小毫秒 -- ARGV[3]: 窗口内请求上限 -- ARGV[4]: 本次请求的唯一标识 local key KEYS[1] local now tonumber(ARGV[1]) local window tonumber(ARGV[2]) local limit tonumber(ARGV[3]) local member ARGV[4] redis.call(ZREMRANGEBYSCORE, key, 0, now - window) local count redis.call(ZCARD, key) if count limit then redis.call(ZADD, key, now, member) redis.call(PEXPIRE, key, math.floor(window / 1000) 60) return 1 else return 0 end注意几个容易出错的地方member必须唯一。如果用同一个值去 ZADDRedis 会当作同一个成员直接覆盖分数导致实际窗口内的请求数被低估。我用的是requestId加上毫秒时间戳拼出来的字符串。如果你用的是 TPM 这种额度的限流其实可以直接把“本次请求的 Token 消耗累加值”当作 score用ZINCRBY而不是ZADD这样统计的是窗口内 Token 总量更贴合上游 TPM 的语义。窗口前移要主动清理。每次请求都先执行ZREMRANGEBYSCORE把过期成员删掉否则集合会无限膨胀。PEXPIRE 的 TTL 我设置成窗口长度加 60 秒比窗口稍长即可保证不提前丢失数据。3.3 分布式环境下的落地细节分布式部署是很多人容易翻车的地方。单机限流用ConcurrentHashMap就行但网关一般是多实例部署每个实例的本地计数器互不可见如果各限各的总流量会随着节点数线性放大。所以生产上必须用集中式的限流存储也就是 Redis。用 Redis 里的滑动窗口天然就是全局的多个网关实例共享同一把闸。但集中式也有集中式的坑。第一个坑是 Redis Cluster 的 key 路由问题。如果同一个用户的相关 key 散落在不同 slot限流逻辑要访问多个 key 时无法保证原子性。解决办法是用 hash tag比如把 key 设计成{rate_limit:user_123}:tpm_window让同一实体的所有 key 路由到同一个 slot。这个我是在一次线上事故之后才补上的当时 Cluster 模式下同一个用户的令牌桶在两个节点上各算各的突发流量直接双倍打进了上游。第二个坑是热点 key。如果某个头部用户请求量巨大他对应的 rate limit key 会成为 Redis 单分片的热点。早期我们先做了一层进程内缓存兜底配合 Redis 里较短的过期时间把 80% 的限流判断拦在本地。但必须想清楚进程内缓存会带来“多实例各自放行”的问题所以我只在“允许少量超发、超发代价可控”的场景用这个策略。对于上游 TPM 这种硬限制永远不搞本地缓存老老实实走 Redis。4. API 配额管理限流之外的预算治理4.1 限流和配额不是一回事先把这个概念掰清楚限流Rate Limit管的是“单位时间内的速率”配额Quota管的是“一段时间内的总量”。用钱来类比限流管的是“你每分钟最多能花多少钱”配额管的是“你这个月一共有多少钱”。你可能每天都很节制、每分钟都没超过额度但一个月累计下来照样会超支。LLM 应用这两件事都必须管。你没有限流峰值会把上游打爆你没有配额月底账单能让你失眠。很多团队一开始只做了限流等到月底账单出来才发现超支了几万块然后才补配额系统。我建议从一开始就把两者分开设计因为数据结构、重置周期、校验时机都不一样。4.2 层级配额记账用户、应用、密钥、全局配额最简单的模型是“一个全局计数器”。但多租户场景下这样做会引发严重的公平性问题某个用户跑一个批量任务几分钟之内就能消耗完当天的全平台 API 预算其他用户全部报“配额不足”。我们的配额设计分四层平台层全平台当日、当月 Token 总预算这是财务红线任何人不能突破。应用层每个应用有自己的每日、每月配额应用和业务线绑定成本拆分靠它。API Key 层每个调用方持有的 Key 有独立的配额。用户层同一个 Key 下还要按用户维度做隔离防止某个用户把 Key 的配额耗光。每一层的配额都独立记账、独立校验。请求进来时四层全部检查任何一层不足都会直接拒绝。这是一个“木桶效应”模型最短的那块板决定请求能不能过。账怎么记我推荐两段式记账请求前预占请求后结算。请求到达网关时先用“预估 Token 数”对四层配额分别做预扣。如果某一层剩余额度不足直接返回 429请求根本不发给上游。请求完成后从上游响应里拿到实际消耗的 Token 数再做差额修正实际比预估多就补扣实际比预估少就回补。这个机制解决了 Token 估算不准的问题也让配额系统能真实反映上游的计费口径。实现上每层配额对应 Redis 里几个 keyquota:{date}:platform:{model}:tokens quota:{date}:app:{appId}:tokens quota:{date}:key:{apiKey}:tokens quota:{date}:user:{userId}:tokens每天零点重置用 Redis 的过期时间自动清理前一天的数据。这里有一个我之前踩过的坑不要用INCR直接加然后用那个值去判断要用DECRBY或预先扣减的方式把“剩余额度”作为存储值这样查询当前余额就一条 GET 命令读写都方便也不用在“用了多少”和“总共多少”之间反复换算。Redis 集群下的原子性问题在这里同样存在。四层配额分别对应多个 key一次请求要同时扣四个 key用简单的方式肯定有部分成功部分失败的风险。我是用一个 Lua 脚本把四层的检查和预扣包在一起脚本里任何一个维度不足就整体回滚保证不会出现“用户额度扣了但全局额度没扣”这种脏账。4.3 配额耗尽后的降级策略配额定死了但业务不能直接瘫。配额耗尽后的处理策略决定了用户体验和系统的鲁棒性。我们按优先级做了几档降级第一档是排队。短时配额不足时让请求进入一个 Redis 队列等有富余配额再放行。排队适合异步任务比如数据批处理、API 写报告这类不要求实时返回的场景。需要特别注意的是排队意味着要设计超时取消否则队列积压用户等到超时体验更差。第二档是降级模型。当前模型配额耗尽了如果业务允许可以自动切换到成本更低或者配额更宽裕的模型上。这需要业务方配合不能网关自己拍脑袋切。第三档是拒绝并告知重试时间。对同步交互类请求排队反而增加延迟不如直接返回 429并在响应的Retry-After头里带上预计可恢复的时间点。客户端根据这个时间去退避重试而不是自己瞎猜。还有一个我强烈建议做的机制配额透支告警。不要等额度变成负数才告警余地会非常小。我在额度还剩 20% 的时候触发黄灯告警剩 10% 的时候触发红灯告警并发给值班群。这样团队有提前量去做变更、扩容或者和业务方确认是否购买更高规格。5. 实战案例给 LLM 网关做一套限流闸门5.1 架构与技术选型我负责的这个项目是一个面向内部多业务的 LLM API 网关技术栈是 Java Spring BootRedis 用的 Cluster 模式。选 Spring Boot 没有特别深奥的理由团队熟悉、生态全真要上高并发也可以靠水平扩容和 Redis 顶住。如果你的团队熟悉 Go用 Go 重写这套逻辑会更轻但核心设计完全一样。限流模块在网关里的位置我放在路由过滤器的第一环认证之后、转发之前。因为限流要读取用户身份和 API Key所以认证必须先行但限流必须在所有业务逻辑之前避免无谓的计算消耗。5.2 核心流程实现一次请求走完限流模块的完整流程是这样的第一步解析请求。从 Header 里拿 API Key从路径里拿模型名和端点类型从请求体里估算 Prompt 的 Token 数。估算用的是一个简单的启发式函数英文字符数除以 4中文字符直接按一个 Token 计再乘以 1.1 的修正系数。精度到 80% 左右就够预扣用了。第二步组合限流维度。限流 key 的粒度是“API Key 模型 端点类型”比如某个客户调用 GPT-4o 的 completions 端点和调用 embeddings 端点走两条完全独立的限流通道。因为上游对这两个端点的配额是分开的混在一起会互相拖累。第三步执行四层配额预扣和两层速率检查。速率检查包括一个令牌桶管秒级突发和一个滑动窗口管分钟级平滑配额检查包括前面说的四个层级。这一步最终会编排成一个 Redis pipeline把四个 Lua 脚本打到 Redis一次网络往返完成所有校验和扣减。第四步全部通过则继续转发。转发完成后从上游响应体或响应头里解析实际 Token 消耗异步做结算修正释放差额到对应配额。伪代码大致长这样public RateLimitDecision check(ClientRequest request) { String bucketKey bucket: request.getApiKey() : request.getModel(); String windowKey window: request.getApiKey() : request.getModel(); long estimatedTokens estimateTokens(request); // 一次性原子完成令牌桶 滑动窗口 四层配额预扣 return redisExecutor.execute(CONSUME_SCRIPT, Arrays.asList(bucketKey, windowKey, windowKey :counter), estimatedTokens, maxBurst, refillRate, now); } public void settle(ClientRequest request, long actualTokens) { // 异步修正actual 和 preReserved 的差额 redisExecutor.execute(SETTLE_SCRIPT, quotaKeys(request), actualTokens - request.getReservedTokens()); }5.3 压测与效果验证这套限流上线后我们对网关做了两轮压测。第一轮测速率控制。把客户端并发拉到 200 线程总 QPS 冲到 300但上游 TPM 配额按 5 万 Token/分钟设置模型平均单请求消耗 800 Token那么理论上最多只能通过约 62 个请求/分钟。实测通过请求数稳定在 58 到 62 之间令牌桶的平滑效果很明显没有出现集群性超发。第二轮测突发吸收。压测脚本在 1 秒内同时发出 50 个请求随后 10 秒无请求再突然发 50 个。令牌桶在第一波突发时放行了一部分之后进入长时间的令牌补充期。上游侧观察到的 TPM 曲线非常平缓再也没有出现过之前那种分钟边界处的双倍突刺。Redis 侧的耗时数据单次限流校验的 P99 在 0.8 毫秒以内这里面包含了 Lua 脚本执行和网络开销。相比一次 LLM 调用本身几百毫秒到几秒的延迟限流消耗完全可以忽略。压测过程中我们还发现了一个有意思的现象因为限流前置上游 429 基本消失了客户端因为 429 引发的重试也消失整个链路的成功率反而比没限流时更高了。这印证了我一直强调的观点限流不是给业务添堵它是让整个链路更稳定的一部分。6. 限流上线后踩过的那些坑6.1 误杀正常请求估算太保守第一次上线时我把 Token 估算的修正系数定成了 1.5本意是多留余量。结果发现不少正常请求在配额还有一大半的时候就被误杀了。原因是估算本身偏大预扣太狠等到实际结算回补时请求都已经被拒绝了。误杀在业务侧的表现是用户明明没怎么用却频繁碰到“配额不足”。这个坑的教训是估算系数和余量要分开看待。估算系数解决的是“我不知道实际会用多少”的问题拉高它会同时放大预扣伤害正常流量。正确做法是尽量优化估算精度余量放到配额值本身去控制而不是靠在估算系数上堆安全性。6.2 Redis 故障引发雪崩更严重的一次是 Redis 集群某节点发生抖动导致部分限流 key 的访问超时。由于我们的实现里 Redis 是强依赖Redis 一抖所有请求都被挡在限流这关整个网关不可用下游业务全部报错。这是一个典型的“保护系统反而拖垮系统”的案例。事后我们加了降级开关当 Redis 不可用时限流模块切换到进程内的本地限流器用一个非常保守的速率兜底。这相当于从“完全放行”和“完全拦截”中间选了一个均衡方案——上游可能吃一点超发流量但至少核心业务不会整体挂掉。这个开关必须可配置、可灰度。默认关闭Redis 连续 3 次超时才自动打开恢复后再自动切回。另外告警一定不能省Redis 降级如果无声无息地发生了你连锅在哪儿都不知道。6.3 突发流量仍然击穿上限某次大促我们做好的令牌桶参数一切正常但上游还是报了一次 429。查了半天发现问题出在客户端。客户端拿到了 429 后用的是一个恶性的重试策略立即重试连续三次失败才放弃。这个策略本来是想提高成功率的但它完全无视了Retry-After头。结果就是网关明明把请求挡下来了客户端疯狂重试网关再挡客户端再重试形成一个高 QPS 的自旋攻击。解决分两头服务端在 429 响应里严格带上Retry-After并保证值合理客户端统一使用指数退避加重试抖动jitter避免所有客户端在同一时刻集中重试。另外我建议大家在压测时专门测一下“429 风暴”场景看看客户端在持续收到 429 时表现是否正常。6.4 租户隔离失效共享 Key 的锅还有一个很容易忽略的场景多个内部业务共用一个 API Key。配额是按 Key 维度的某个业务跑大批量任务时会把共用 Key 的配额全部耗尽其他依赖同一 Key 的业务全部被限流。排查起来非常费劲因为从 Key 维度看一切正常只是“配额用光了”。后来我在记账维度里加了应用层和用户层并且在网关日志里把每个请求的appId、userId、配额消耗、剩余额度全部打出来才定位到是个别批量任务惹的祸。这个教训促使我在这套系统的日志规范上下了功夫限流模块的日志必须有足够的上下文包括命中了哪个维度、哪个 key、剩余多少、预扣多少、实际消耗多少。没有这些日志限流问题排查起来就是大海捞针。6.5 排查限流问题我常用的方法最后分享几个排障工具和方法。Redis 侧redis-cli --bigkeys可以快速找出异常膨胀的限流 key比如滑动窗口日志里时间戳成员数量暴涨多半是窗口清理逻辑出了问题。MONITOR命令在低峰期也可以临时打开看限流脚本的调用频率但在生产高峰千万不要开性能影响太大。监控侧我重点盯四个指标限流拒绝率、各维度配额剩余量、Redis 限流耗时、429 响应占比。这四个指标里前两个是业务健康度后两个是系统健康度。我见过只盯“拒绝率”不看“配额剩余”的团队结果配额早就见底了拒绝率却迟迟没有反映出来。日志侧我要求每个限流决策都记录决策结果和原因代码比如TPM_LIMIT、TOKEN_BUCKET、APP_QUOTA。这样一旦线上出问题直接按原因代码聚合日志几分钟就能定位是哪个维度被限了。最后分享一点体会限流这套东西算法本身不难难的是你永远在跟“不确定性”打交道Token 消耗不确定、用户流量不确定、上游配额不确定、Redis 的稳定性也不确定。我的核心经验是宁可一开始用简单可靠的方案也不要急着上花哨的分布式限流框架。先把 Redis 加 Lua 这套跑稳把配额记账的模型理清楚把日志和监控补齐就已经能应对绝大多数生产问题。去年我犯的最大错误是花了很多时间纠结“令牌桶和滑动窗口到底谁更优雅”结果忽略了参数调优和降级策略上线第一周就被真实流量教育了一顿。现在回头看这些算法没有银弹它们的价值取决于你对业务的理解深度和对异常场景的准备程度。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →