# 5.17 接口限流 :::tip 概述 接口防护采用固定窗口限流(middleware/ratelimit.go):按 IP + 路径计数,普通接口与认证入口双阈值。计数经 Redis 跨实例共享,存储异常时放行(限流不阻断业务)。 ::: ## 配置 ```yaml rateLimit: enable: true # 是否启用限流 limit: 120 # 普通接口每窗口每 IP 每路径最大请求数,0 关闭普通限流 windowSeconds: 60 # 计数窗口(秒) authLimit: 10 # 登录/验证码/令牌兑换每窗口每 IP 最大请求数(更严格) ``` ## 规则明细 ```text 作用范围: * 仅限 /api 前缀路径;/api/health 显式豁免 * /api.json、/swagger、静态资源不带 /api/ 前缀,自然不受限 双阈值: * 普通接口:limit(默认 120 次/窗口/IP/路径) * 认证入口(/api/login、/api/captcha、/api/oauth2/token):authLimit(默认 10 次/窗口/IP) 计数键:ratelimit:{clientIP}:{path}:{窗口起点} 窗口起点 = Unix/window*window 对齐(固定窗口语义) TTL = window+5s(仅用于回收,不影响窗口归属) 计数实现:incrCounter——GET→SET 自增;不存在时 SET NX 抢占; 并发下 NX 失败再读现值加一 计数存储:utility/cache(Redis 优先、内存降级)→ 多实例共享计数 超限响应:code=429 信封(CodeTooManyRequests) 存储异常:放行(限流不阻断业务) ``` ## 真实 IP(防绕过) ```text util.go realClientIP: 直连来源不在 api.proxyIps(逗号分隔,精确 IP 与 CIDR)内 → 直接用 GetRemoteIp()(不信任任何转发头) 在列表内 → 才信任 X-Forwarded-For 首段 / X-Real-IP 目的:防止伪造 XFF 绕过限流或污染日志 部署:Nginx 反代务必把反代网段加入 api.proxyIps,否则全站记反代 IP ``` ## 固定窗口的语义与边界 ```text 固定窗口特点:窗口边界处可能出现 2 倍瞬时放行(如 59s 放 120 + 61s 放 120) 本项目取舍:实现简单、Redis 计数成本低;对管理后台场景足够 (非支付级防刷场景,无需滑动窗口/令牌桶) 限流粒度:IP + 路径 —— 同 IP 调不同路径各自计数 ``` ## 与整体防护的关系 ```text 接口限流(本节) IP 维度,防刷与暴力破解 + 认证专项 authLimit 独立严阈值(验证码/登录/令牌) + 演示模式 demo=true 拦截全部写操作 + 权限校验 403 拒绝未授权 + jti 黑名单 已撤销令牌即时失效 ``` ## 调参与排障 | 场景 | 处理 | |------|------| | 正常用户被误伤(429) | 压测/办公出口 IP 共享场景调大 limit;authLimit 不建议调大 | | 限流不生效 | enable=false?内存降级下单机计数仍应生效;确认路径带 /api 前缀 | | 多实例阈值翻倍 | Redis 不可用内存降级(各实例各自计数)——查 /api/cache/info | | 反代后全站共享阈值 | api.proxyIps 未含反代出口(全站记反代 IP,共同一个桶) | | 突发 429 后恢复慢 | 固定窗口未到窗口起点;键 TTL=window+5s 保证自动回收 | ::: warning 生产建议 + authLimit(认证入口 10 次/分钟/IP)是防暴力破解的关键阈值,不建议调大 + 限流计数依赖 Redis——内存降级时多实例部署下实际阈值 = 配置值 × 实例数 + 压测前临时调大 limit 或按压测 IP 加白(受信代理配置不等同限流白名单,需另行处理) :::