限流与日志
预计阅读时间: 8 分钟 预计阅读时间: 8 分钟限流用于保护高频接口,避免单个用户、IP 或调用方在短时间内打满服务资源。日志用于记录管理后台里的关键操作,让登录、增删改、资源访问、异常和耗时都有迹可查。
Summerrs Admin 提供两类能力:
声明式限流
#[rate_limit] 用在 HTTP handler 上。它会在业务逻辑执行前完成限流检查,命中时直接返回 429 Too Many Requests;Redis 后端不可用且策略为 fail_closed 时返回 503 Service Unavailable。
最常见的是按 IP 或用户限流:
使用时注意:
- 只能用于 async free function。
- 不支持带
self的方法。 - 必须放在路由宏外层,例如
#[rate_limit]写在#[get_api]上方。
接入前置条件
限流宏依赖 summer_common::rate_limit::RateLimitEngine。业务接口启用限流前,需要先在应用里提供这个组件。
可以通过插件注册:
RateLimitPlugin 会从 app 组件里取 summer_redis::Redis,然后注册:
也可以在自定义 router 或测试场景里,直接把 RateLimitEngine 作为 axum extension 或 summer 组件注入。
如果还想自动写响应头,需要给 router 挂:
参数
key = "user" 会优先使用登录用户 ID;未登录时回退到 ip:<client_ip>,不会和 key = "ip" 串桶。key = "header:X-Tenant-Id" 会按 Header 值分桶,Header 缺失时使用 unknown。
算法
只有 token_bucket 和 gcra 支持 cost-based 限流和 reservation。
Redis 故障策略
backend = "redis" 时,Redis 故障由 failure_policy 决定:
多实例部署时,fallback_memory 只在单个进程内计数,语义会降级。对外部 API 更看重可用性时通常选 fail_open;对配额严格性要求更高时选 fail_closed。
Shadow 模式
mode = "shadow" 表示只记录“本来会拒绝”,但不真的拒绝请求:
Shadow 和 Enforce 共享同一个桶状态。长期跑 shadow 后直接切到 enforce,可能立刻拒绝一段时间。切换前可以调用 RateLimitEngine::reset_key 清桶。
Cost 与预扣
RateLimitContext 还支持按 cost 消耗配额:
如果业务需要“先预扣,结束后按真实消耗找平”,使用 reserve:
这类能力适合 AI token、批量导入、文件处理等消耗型任务。非 GCRA 算法收到 cost > 1 时只按 1 次请求计数,并首次打 warn。
响应头
如果挂了 rate_limit_headers_middleware,响应会自动带 IETF draft 风格的限流头:
被限流时,业务响应使用统一错误格式:
操作日志宏
系统接口可以使用 #[log] 记录操作审计:
宏展开后会注入 OperationLogContext,记录:
敏感接口要关闭参数记录:
大响应或文件响应要关闭响应体记录:
日志批量写入
主应用已注册:
LogBatchCollectorPlugin 提供 OperationLogCollector 和 LoginLogCollector。service 不同步写库,而是先 try_send 到有界 channel,后台 worker 满足条件后批量 insert_many。
默认配置来自 summer-plugins/src/log_batch_collector/config.rs:
通道满或关闭时,日志会被丢弃并记录 warn,不会阻塞主请求。
查询日志
系统路由提供日志查询:
日志查询本身也挂了 #[log],所以管理员查看日志的动作也会进入操作日志。
实践建议
- 普通后台 CRUD 优先只用
#[log]和权限宏。 - 登录、密码、token、文件下载等敏感/大体积接口要关闭参数或响应体记录。
- 限流上线前先注册
RateLimitEngine,再从少量接口开始。 - 外部 API 或高成本接口优先使用 Redis 后端。
- 从
shadow模式观察一段时间再切enforce,切换前注意清桶。 - 如果需要客户端自动退避,别忘了挂
rate_limit_headers_middleware。
