Files
junhong_cmp_fiber/openspec/changes/add-carrier-gateway-rate-limit/tasks.md
邹进 02a842f066
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m35s
fix: 网关限流系统配置 Key 改为三段式,修复注册失败
- 受控系统配置要求 Key 至少三段,原两段式 Key(如 polling.rate_limit_mode)注册被拒,后台不可见、限流器只能使用默认值
- Key 改为 polling.gateway.* 与 polling.stop_resume_record.*;新增迁移 000247 将已落库的旧 Key 改名并保留取值
- 后续提案中规划的开关同步改为三段式(polling.scheduler.mode、polling.stop_resume.queue_enabled)
2026-10-24 16:17:34 +09:00

6.7 KiB
Raw Blame History

1. 迁移与模型

  • 1.1 新增成对迁移:建 tb_carrier_rate_limit_group(carrier_id 可空表示全局组、group_code、group_name、max_qps、operations、high_priority_operations、reserved_qps、status、审计字段);down 删表。
  • 1.2 新增成对迁移:建 tb_gateway_error_classification(关键字、匹配方式、类别、适用操作、优先级、启用状态),写入方案第 6.2 节的初始规则;down 删表。
  • 1.3 新增成对迁移:建分区表 tb_card_stop_resume_record(按 created_at 月分区,主键 (id, created_at),DEFAULT 分区,当前月起若干个月分区,索引 (card_id, created_at)、(carrier_id, created_at)、(result, created_at));down 删表。
  • 1.4 新增成对迁移:tb_system_config 插入 module = polling 配置项(polling.gateway.rate_limit_mode 默认 observe、rate_limited_retry_delay、限流累计推迟告警上限、默认 QPS、停复机记录保留月数与提前建分区月数);down 删除这些键。
  • 1.5 新增对应 model 与 Store;internal/infrastructure/systemconfig 注册上述键(类型、取值范围、枚举),SystemConfigListRequest.Module 枚举增加 polling;为 JSON 类型注册项增加结构校验接缝。

2. 网关结构化错误与停复机不重试

  • 2.1 internal/gateway:新增 GatewayError(类别、业务码、信息、耗时),executeHTTPRequest 与业务错误分支返回它并保留现有错误码;上游信息脱敏截断。
  • 2.2 flow_card.go:StopCard / StartCard 改为 maxRetries = 0(同 SetCardSpeedTier 写法)。
  • 2.3 全仓检查对网关错误字符串的解析(如按 Gateway 业务错误: code= 匹配),改为读取结构化字段,行为保持一致。

3. 限流器

  • 3.1 新增限流器:滑动窗口 Lua(账户组 + 全局组 + 预留额度 + high_waiting),返回放行或需等待毫秒数;提供立即返回与等待至超时两种调用方式。
  • 3.2 限流组配置读取:数据库 → Redis 缓存,变更后即时刷新;按目标卡 carrier_id + 操作查组,未覆盖或停用回落默认 QPS。
  • 3.3 实现限流 AttemptObserver,在 BeforeAttempt 取名额;与现有审计观察器链式组合;拿不到名额返回 ErrRateLimited(结构化错误类别"限流")。
  • 3.4 豁免标记:ctx 携带豁免来源;在后台人工点击停复机、通道阈值、自动续费、手动刷新 4 处入口打标记,其余不改。
  • 3.5 observe / enforce 模式:observe 只计数(含本应被限流次数)不拦截;模式读取受控系统配置并即时生效。

4. Asynq 遇限流的重试语义

  • 4.1 pkg/queue/server.go:IsFailure 对 ErrRateLimited 返回 false;RetryDelayFunc 对其返回 rate_limited_retry_delay,其余回落 DefaultRetryDelayFunc。
  • 4.2 现有轮询 handler(internal/task/polling_*_handler.go)遇 ErrRateLimited 按短延迟 Requeue、不计失败、不写失败审计。
  • 4.3 限流累计推迟告警:记录任务首次被限流时间,超过上限写告警,任务继续重试。

5. 错误分类与调用计数

  • 5.1 错误分类器:按适用操作、启用状态、优先级匹配关键字;规则缓存于 Redis 并在变更后刷新;未命中归业务错误。
  • 5.2 网关调用分钟计数:每次尝试结束按"账户 × 操作 × 结果类别"计数并记录耗时分桶,含豁免调用量与本应被限流次数。
  • 5.3 卡上最近错误:查询失败时写该卡该检查类型的最近错误(类别、码、信息、时间)。

6. 停复机记录替代审计

  • 6.1 每次实际调用上游停机或复机后写停复机记录(来源、场景、尝试序号、结果、错误类别、上游信息、耗时);写入失败只记日志与计数。
  • 6.2 系统自动停复机不再写 tb_audit_event 与集成日志;后台人工点击停复机保留统一审计并同时写记录。
  • 6.3 轮询查询删除集成日志写入(含有变化时的成功记录),业务事实变化的持久化不变。
  • 6.4 Worker 定时任务:提前创建未来分区、按保留期删除过期分区,失败写告警。

7. 后台接口

  • 7.1 限流组:GET/POST /polling-rate-limit-groups、GET/PUT/DELETE /polling-rate-limit-groups/:id、PUT /:id/status;保存校验(reserved_qps < max_qps、高优先级属于本组、同账户同操作只属一组);修改仅超级管理员并写统一审计。
  • 7.2 错误分类:GET/POST /gateway-error-classifications、PUT/DELETE /:id、PUT /:id/status、POST /gateway-error-classifications/test。
  • 7.3 停复机记录:GET /card-stop-resume-records(时间范围必填且有上限)、GET /iot-cards/:id/stop-resume-records。
  • 7.4 调用量统计:GET /polling-stats/carriers(实际 QPS、成功率、按类别失败数、p95、本应被限流次数、豁免调用量)与 GET /polling-stats/carriers/:carrier_id/trend。
  • 7.5 枚举:GET /polling-options(操作类型、错误类别、匹配方式等)。
  • 7.6 同步可执行路由、cmd/api/docs.go、cmd/gendocs/main.go,运行 go run cmd/gendocs/main.go 重新生成接口文档。

8. 编译与规范校验

  • 8.1 gofmt -w 变更文件;go build ./cmd/api ./cmd/worker 通过。
  • 8.2 openspec validate add-carrier-gateway-rate-limit 与 openspec doctor --json 通过。

9. 测试环境验证(自动化测试为 N/A,按 ENG-TEST-001 在维护者指定测试环境逐项核对)

  • 9.1 迁移在测试库执行成功;未来分区与 DEFAULT 分区存在。
  • 9.2 observe:超过限流组上限的请求照常发出,统计接口显示本应被限流次数。
  • 9.3 enforce:账户组用满时请求被拒;不同账户互不影响;停用组回落默认 QPS;全局组生效;预留额度下停机仍可放行。
  • 9.4 内部重试计量:构造查询超时,确认第二次尝试单独取名额。
  • 9.5 停复机超时只发一次请求,返回结果未知;后台人工点击不受限流。
  • 9.6 Outbox 消费者遇限流:Asynq 重试计数不增加、按短延迟重试;非限流错误按原规则重试;累计推迟超限写告警。
  • 9.7 错误分类:测试接口与实际调用的分类一致;"密码错误"归账户配置错误,"已存在"不归已是目标状态。
  • 9.8 系统自动停复机只写停复机记录、不写统一审计与集成日志;人工点击两者都写;记录写入失败不影响卡状态回写。
  • 9.9 生成的接口文档包含新增接口;/system-configs?module=polling 返回新增配置项且 JSON 结构校验生效。