笃行
首页
个人 & 心法
互联网/硬件后台
游戏基础架构
UE 引擎
游戏业务
AI / 大模型
数据结构与算法
机器学习数学
通用基础
GitHub
首页
个人 & 心法
互联网/硬件后台
游戏基础架构
UE 引擎
游戏业务
AI / 大模型
数据结构与算法
机器学习数学
通用基础
GitHub
  • 接入与通信

    • 游戏基础架构与工具
    • 接入网关(长连接接入层)
    • 消息总线(共享内存 IPC)
    • 帧同步(Lockstep)
    • 全栈极限低延迟(物理层→应用层)
    • KCP 与 QUIC
  • 网络与服务网格

    • CNI 与 K8s 网络插件
    • 容器运行时:Docker 与 containerd
    • Istio 与 Cilium 服务网格
    • 服务网格:中心化 vs 去中心化
    • eBPF 原理与在网络/可观测/安全的落地
    • 自研 Mesh 服务网格 × K8s 部署
    • 自研 Mesh × K8s · 数据面深水区
    • Tco 协程框架 · 运行时深水区
    • K8s 异构 & 网络插件
  • 有状态服务

    • 分布式游戏存储(分布式 KV)
    • 有状态服务的数据迁移
    • 有状态服务的数据恢复与容灾
    • 有状态服务的 K8s 编排层治理(防漂移 + 节点崩溃恢复)
    • 内存配置热刷新与更新机制
  • 算法与协程

    • 一致性哈希四算法实现(Ring Hash / Ketama / Maglev / Jump Hash)
    • 蓄水池抽样(Reservoir Sampling)与加权扩展
    • C++ 协程:有栈 vs 无栈 vs C++20 原生(函数染色与 libco)
    • Raft 与 Gossip:CP 强一致共识 vs AP 最终一致传播
  • 流量与承载

    • 游戏秒杀场景承载
    • 令牌桶与漏桶
    • 限流与熔断
    • 限流:互联网 vs 游戏
    • 灰度发布 · Canary / BlueGreen / A-B / Shadow
    • 服务器日常系统开发与维护 SOP
    • 极限发压工具设计(多长连接 + 发压任务)
    • 性能分析与优化方法论(六条排查线)
  • 编译与排查

    • 编译优化与 LLVM/Clang/GCC
    • Sanitizer 工具链与内存泄漏定位

限流:互联网 vs 游戏

两种世界的两种解法 · 无状态 vs 强状态

一句话对比

互联网:请求彼此独立,用令牌桶/漏桶 + Redis+Lua 集群限流控 QPS,超限直接拒或降级;游戏:世界状态高度耦合,用 Tick 内 CD + 分层配额 + 排队机 + 帧摊派 控节奏,玩家看到的是"排队"而不是 429。

场景问题

限流的本质是保护系统 + 塑造用户体验:

  • 保护系统:防止下游被打爆(DB 连接池、外部 API 配额)
  • 塑造体验:秒杀、活动开服、直播抢购的公平性与可预期时延

面对高并发洪峰时,互联网世界和游戏世界的请求形态完全不同:一个是无状态的独立请求洪流,一个是彼此高度耦合的世界状态更新。同一个"限流"命题,落到两个世界会长出截然不同的解法。

打个比方:互联网限流像高速收费站——车太多就少开几个道、超载的直接劝返(拒绝 / 429),反正每辆车互不相干,赶回去重来也就重来了。游戏限流像热门游乐园——你总不能把兴冲冲进园的玩家"劝返"(体验直接爆炸),只能发号、排队、分批放行(排队机),因为园区里所有人共享同一个世界(同一副本、同一排行榜、同一场活动),必须控节奏而不是简单拒绝。类比失效边界:这个对比不是说"游戏就不用令牌桶了"——游戏的接入层、跨服网关照样拿令牌桶挡洪水(桶算法详见 令牌桶与漏桶)。差别只在面向玩家的那一层:互联网对独立请求可以干脆利落地拒,游戏因世界状态耦合,更多是把"拒绝"翻译成"排队 / 帧摊派"这类延迟,让玩家看到的是转圈等待而非一记闭门羹。

四大经典算法

算法特点适用场景
计数器固定窗口,简单,边界毛刺粗粒度接口保护
滑动窗口把窗口切分为小格子,逐格滚动需要平滑的 QPS 统计
漏桶恒定流出速率,削峰但不允许突发严格匀速的下游(DB 写、老接口)
令牌桶恒定填充速率、允许桶容量内的突发最常用,Sentinel/Guava/Nginx 都用它

四大算法的逐行实现、数学推导与 Redis+Lua 落地详见 令牌桶与漏桶;限流之后的熔断/降级/兜底详见 限流与熔断。本篇聚焦"互联网 vs 游戏"两种世界的选型与形态差异,不重复展开桶算法细节。

实现方案

互联网侧:分层限流的常见组合

接入层(网关/Nginx) → 服务层(框架内) → 资源层(DB/Cache/外部 API)

  • Nginx limit_req:漏桶算法,按 IP/URI 限;配 burst 允许瞬时突发
  • Nginx limit_conn:并发连接数限制
  • API 网关(Kong/APISIX/Higress/Envoy):分布式令牌桶,Redis 集群同步计数
  • Sentinel(阿里):QPS/线程数、系统自适应、集群限流、热点参数限流、熔断降级一体
  • Guava RateLimiter:单机令牌桶,简单场景合适,无法跨进程
  • Resilience4j / Bucket4j:Java 生态里较轻的替代

分布式限流有两条路。

1. Redis + Lua 原子脚本(主流)

-- KEYS[1] 限流 key, ARGV[1] 令牌桶容量, ARGV[2] 每毫秒填充速率, ARGV[3] 请求令牌数
local now = tonumber(redis.call("time")[1]) * 1000 + math.floor(tonumber(redis.call("time")[2])/1000)
local last = tonumber(redis.call("hget", KEYS[1], "last") or now)
local tokens = tonumber(redis.call("hget", KEYS[1], "tokens") or ARGV[1])
tokens = math.min(tonumber(ARGV[1]), tokens + (now - last) * tonumber(ARGV[2]))
if tokens < tonumber(ARGV[3]) then return 0 end
tokens = tokens - tonumber(ARGV[3])
redis.call("hmset", KEYS[1], "last", now, "tokens", tokens)
redis.call("pexpire", KEYS[1], 60000)
return 1
  • 优点:一致强、跨语言、和网关/服务/中台通用
  • 坑:Redis 单点热 key(一致性哈希打散);网络 RTT(近端本地令牌桶 + 定期回填 Redis "批采令牌")

2. 集群限流:中心配额 + 本地精算

  • 中心(Sentinel Token Server / 独立 quota 服务)分发配额
  • 每个节点消费配额,用完向中心申请下一批
  • Netflix concurrency-limits / Google Doorman 都是这个思路

令牌桶的判定流程如下:

游戏侧:多层次的"节奏控制"

游戏没有"429",只有"排队"、"CD"、"进度条"——限流融进玩法里。

  • Tick 内 CD:技能 CD、聊天冷却、跨服请求 CD——服务端记录 last_use_time,Tick 循环检查
  • 玩家 / 账号 / 服务器 / 大区多层配额:单玩家上限 + 服务器总量上限 + 大区总量上限,三层拦截避免热点账号打爆整台服务器
  • 按帧摊派 (Time-slicing):一次要处理 10w 玩家排行榜刷新——每帧只处理 1000 个,10 帧摊完;避免单帧卡顿导致游戏 FPS 掉
  • 写队列削峰 + 落地合并:玩家背包变更先塞队列,异步批量写 DB;同一玩家多次变更合并为一条
  • 活动开服的排队机 + 平滑扩容:玩家进入前先分配令牌(token),登录服控排队;后端根据实时容量下发放行速率;扩容时不是全量放行,而是按扩容节点数按比例放开
  • 跨服玩法排队匹配:MMR 匹配池 + 时间等待惩罚,本质也是限流(分批入场)

为什么这么做

组合拳的顺序是限流 → 熔断 → 降级 → 兜底,每一层触发时机与用户感知都不同:

阶段触发时机手段用户感知
限流QPS 超阈值拒绝新请求429 / 排队
熔断错误率超阈值直接短路,不打下游快速失败
降级依赖不可用走缓存/静态兜底/本地默认值部分能力缺失
兜底一切皆炸前端友好提示,避免白屏"服务繁忙请稍后"

核心决策清单:

  • 哪层拦? 网关粗粒度 + 服务层细粒度
  • 对谁拦? 用户/租户/接口/来源 IP/参数值
  • 拦多少? 从压测容量倒推,留 20~30% buffer
  • 怎么算? 令牌桶(允许突发)or 漏桶(严格匀速)
  • 超限做什么? 拒 vs 排队 vs 降级 vs 兜底
  • 可观测? QPS、拦截率、桶剩余令牌、排队时长——都要埋点

为什么别的选择不行

互联网侧常见坑:

  • 网关限流粒度过粗:整个 API 一刀切,误伤高价值用户 → 分用户等级、租户、渠道多维度限流
  • 令牌桶补偿机制忘做:突发场景桶被打空后长期饥饿 → 桶容量 = QPS × N 秒
  • Redis + Lua 集群模式踩 Slot:Lua 用多 key 会报错 → Hash Tag 强制同 Slot
  • 限流降级链路混乱:限流后不知道该返 429 还是走降级 → 明确 SLA:快速失败 vs 排队 vs 降级 vs 兜底
  • 压测没打限流:压测被限流削平,误判系统能扛 → 压测环境关闭或提高阈值

游戏侧常见坑:

  • 单玩家做 CD 但漏了账号:脚本用一个账号连开多个客户端绕过 → 加账号级 QPS
  • Tick 内长任务卡帧:一次刷排行榜卡 3 帧 → 按帧摊派 + 优先级队列
  • 活动开服炸机:所有人 0 点冲进来,排队机压垮登录服 → 排队机独立集群 + 令牌预分配 + 客户端本地倒计时错峰
  • 抢购超卖:并发扣库存没上锁 → Redis Lua 原子扣减 + DB 乐观锁双保险
  • 跨服匹配挤爆:所有大区打一个匹配池 → 分区匹配 + 分层匹配 (MMR × 时长)

沉淀结论

两个世界的本质差异:

维度互联网游戏
请求耦合独立无状态世界状态高度耦合
密集类型I/O 密集CPU 计算密集
限流单位QPS / QPMTick / 帧 / 玩家配额
超限反馈429 / 降级排队 / CD / 进度条
分布式协调Redis+Lua / 中心配额单服权威 + 分层配额
可观测重点拦截率、延时、错误码分布帧时长、队列长度、CD 命中率

记忆口诀

令牌桶四要素:容量 / 速率 / 补偿 / 原子扣
游戏限流五层:Tick-CD → 玩家配额 → 服务器配额 → 大区配额 → 排队机
超限决策口诀:先限流(拒)→ 再熔断(断)→ 再降级(缓)→ 最后兜底(页)

内容来源

迁移自 guide/theme-rate-limit(综合整理)。原始出处:综合整理 Sentinel、Nginx、游戏后台经验,具体实现请以官方文档为准(2026-07)。

自测:合上资料能说清楚吗?

  1. 令牌桶和漏桶的本质区别是什么?各适合什么场景?
参考答案

漏桶:恒定流出速率,不允许突发,适合严格匀速下游(DB写入);令牌桶:恒定填充速率,允许桶内令牌积累消费突发,适合大多数 API 限流场景。关键差异:令牌桶允许突发,漏桶不允许。

  1. Redis + Lua 分布式令牌桶,为什么用 Lua 脚本而不是普通命令序列?
参考答案

Lua 脚本在 Redis 中原子执行,「读取令牌数 → 计算补充 → 判断 → 扣减 → 写回」整个流程不会被其他命令插入,避免 TOCTOU(检查到使用之间的竞态)。普通命令序列即便加 WATCH/MULTI 也会在高并发下频繁重试。

  1. 游戏服务器为什么不用 429,而是用排队和 CD 来表现限流?
参考答案

游戏世界状态高度耦合,请求不能简单丢弃(丢弃=玩家操作丢失,体验极差);用排队/CD 把「等待」内化为游戏机制,玩家感知为「正常等待」而非「系统故障」。本质:游戏是计算密集 + 强状态,互联网是 I/O 密集 + 无状态。

  1. 活动开服时「排队机 + 平滑扩容」方案,为什么扩容时不能全量放行?
参考答案

新节点启动需要预热(JIT/缓存/连接池),全量放行会瞬间打爆刚启动的节点。正确做法:按新增节点数等比例放行,确保每个节点承受的 QPS 始终在安全阈值内,「扩一批,放一批」。

  1. 对比「中心配额分发」和「Redis+Lua 单 key 限流」两种方案,各有什么取舍?
参考答案

Redis+Lua:简单、延迟低,但热 key 打爆 Redis 单节点,需 Hash Tag 分散;中心配额(Sentinel Token Server / Doorman):节点本地消费配额,减少 Redis 压力,但中心节点挂了需降级为本地限流,实现更复杂。QPS < 10w 用 Redis+Lua,超大流量用中心配额。

最近更新: 2026/9/10 11:38
Prev
限流与熔断
Next
灰度发布 · Canary / BlueGreen / A-B / Shadow