token-quota
本页由项目自动归档生成,源码与最新进展以 GitHub 仓库为准。
项目简介
一个运行在 OpenResty 层的 LLM 网关 Token 配额限流模块,基于 Lua + Redis + MySQL 实现。它能在请求打到模型之前按「API / APP / Model」三级、按日/月/年/自定义周期对 prompt_tokens 和 completion_tokens 做限流,并在超配额时直接拦截(返回 429),适合需要把模型调用成本控制在网关层、避免超额推理开销的场景。
技术栈
| 维度 | 技术 |
|---|---|
| 主要语言/框架 | Lua(OpenResty / ngx_lua),Shell(启停与测试脚本) |
| 关键依赖/库 | lua-resty-redis(配置 key 单机连接池)、lua-resty-redis-cluster(计数器集群)、OpenResty 内置 cosocket 与 lua_shared_dict、自封装 MySQL 连接池 |
| 数据存储 | Redis 单机(配额配置 key,后缀 :qos)+ Redis 集群(计数器 / :DANGER / :STATUS 等高并发 key)+ MySQL(quota_periods 表持久化自定义周期元数据) |
| 运行/部署方式 | OpenResty:./start.sh → openresty -p . -c conf/nginx.conf;代理与管理 API 监听 8000,Mock 后端监听 9001;依赖 Redis、MySQL 与 lua-resty-redis-cluster |
核心功能 / 亮点
- 三级别配额:API / APP / Model 独立限流,key 由
lua/token_quota/key_builder.lua按级别构建。 - 多周期 + ISO 8601 自定义周期:
day/month/year加P45D/P3M/P2Y(lua/token_quota/period_parser.lua解析),混合策略:P3M按自然月、P45D按固定天数计算。 - 双路径机制(安全期 / 危险期):使用率 < 80% 走内存累积(
buffer.lua)由sync_timer.lua每秒批量同步;≥ 80% 进入危险期,由settlement.lua经ngx.timer.at实时直写,防超额。 - Redis 双存储架构:配置 key 存单机、计数器 key 存集群(
redis_client.lua与redis_cluster_client.lua),读写模式分离。 - 周期元数据持久化 MySQL + 可恢复:
period_store.lua+mysql_client.lua+sql/schema.sql,Redis key 丢失后能从quota_periods恢复周期起点;自定义周期为滚动窗口,从首次请求计时。 - 接入控制预扣 + 超限拦截:
admission.lua预扣 token,超限返回429(quota_exceeded);settlement.lua按实际 usage 修正差值。 - 容错降级放行:Redis / MySQL 不可用时 fail-open(放行并记录告警),保证旁路组件故障不影响主链路。
- 管理 REST API:
admin.lua提供配额配置 CRUD、状态查询、内存 buffer 查看、健康检查(/admin/quota/config、/admin/quota/status、/admin/quota/buffer、/health)。
目录结构
1 | token-quota/ |
相关链接
- GitHub 仓库:token-quota
补充:为什么把配额做在网关层
Token 配额看起来是个计数问题,但放在哪一层做,效果差别很大。这个模块做在 OpenResty 层,核心理由是超配额的请求根本不应该打到模型——在网关层拦截,省掉的是一整次推理的算力开销;如果在应用层做,钱已经花出去了。
几个值得记下来的设计决策:
- 双路径机制:安全期(离阈值还远)用内存累积计数,追求快;进入危险期切换到实时结算,追求准。这是典型的「用精度换性能、但在关键时刻不妥协」的取舍。
- Redis 双存储:配置 key 存单机、计数器 key 存集群。因为这两种数据的访问模式完全不同——配置是低频强一致读,计数是高频高并发写,塞进同一个实例会互相拖累。
- 周期元数据落 MySQL:Redis 的 key 有过期和丢失风险,但用户的配额周期(比如 P45D 从哪天开始算)丢不起。用 MySQL 存元数据,Redis key 没了也能算回来。
- ISO 8601 Duration 自定义周期:支持
P45D、P3M、P2Y这类写法,且做了混合策略——P3M按自然月算,P45D按固定天数算。这两者对用户来说都是「3 个月」,但结算边界完全不同,必须分开处理。 - 降级放行而非拒绝:Redis 或 MySQL 不可用时选择 fail-open(放行)。限流系统在这里有个经典抉择:宁可短暂超发,也不能让整个 API 因为一个旁路组件挂掉而全站不可用。