feat: 实现 IoT 卡轮询系统(支持千万级卡规模)
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m35s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m35s
实现功能: - 实名状态检查轮询(可配置间隔) - 卡流量检查轮询(支持跨月流量追踪) - 套餐检查与超额自动停机 - 分布式并发控制(Redis 信号量) - 手动触发轮询(单卡/批量/条件筛选) - 数据清理配置与执行 - 告警规则与历史记录 - 实时监控统计(队列/性能/并发) 性能优化: - Redis 缓存卡信息,减少 DB 查询 - Pipeline 批量写入 Redis - 异步流量记录写入 - 渐进式初始化(10万卡/批) 压测工具(scripts/benchmark/): - Mock Gateway 模拟上游服务 - 测试卡生成器 - 配置初始化脚本 - 实时监控脚本 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
196
docs/polling-system/README.md
Normal file
196
docs/polling-system/README.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# 轮询系统
|
||||
|
||||
## 概述
|
||||
|
||||
轮询系统是 IoT 卡管理平台的核心模块,负责定期检查卡的实名状态、流量使用情况和套餐流量余额。系统采用分布式架构,支持高并发处理和动态配置。
|
||||
|
||||
## 核心功能
|
||||
|
||||
### 1. 实名检查轮询(Realname Check)
|
||||
|
||||
- 定期查询卡的实名认证状态
|
||||
- 自动跳过行业卡(无需实名)
|
||||
- 状态变化时重新匹配配置
|
||||
|
||||
### 2. 流量检查轮询(Carddata Check)
|
||||
|
||||
- 定期查询卡的流量使用情况
|
||||
- 支持跨月流量自动重置
|
||||
- 记录流量使用历史
|
||||
|
||||
### 3. 套餐检查轮询(Package Check)
|
||||
|
||||
- 监控套餐流量使用率
|
||||
- 超额自动停机(>100%)
|
||||
- 临近超额预警(>=95%)
|
||||
|
||||
## 系统架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Worker 进程 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Scheduler │ │ AlertChecker │ │ CleanupTask │ │
|
||||
│ │ 调度器 │ │ 告警检查器 │ │ 清理任务 │ │
|
||||
│ └──────┬───────┘ └──────────────┘ └──────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ Asynq 任务队列 │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Realname │ │ Carddata │ │ Package │ │
|
||||
│ │ Handler │ │ Handler │ │ Handler │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Redis │
|
||||
│ - 轮询队列 (Sorted Set) │
|
||||
│ - 手动触发队列 (List) │
|
||||
│ - 卡信息缓存 (Hash) │
|
||||
│ - 配置缓存 │
|
||||
│ - 并发信号量 │
|
||||
│ - 监控统计 │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ PostgreSQL │
|
||||
│ - tb_polling_config │
|
||||
│ - tb_polling_concurrency_config │
|
||||
│ - tb_polling_alert_rule │
|
||||
│ - tb_polling_alert_history │
|
||||
│ - tb_data_cleanup_config │
|
||||
│ - tb_data_cleanup_log │
|
||||
│ - tb_polling_manual_trigger_log │
|
||||
│ - tb_data_usage_record │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 快速启动
|
||||
|
||||
### 1. 数据库迁移
|
||||
|
||||
```bash
|
||||
make migrate-up
|
||||
```
|
||||
|
||||
### 2. 初始化配置
|
||||
|
||||
```bash
|
||||
psql $DATABASE_URL -f scripts/init_polling_config.sql
|
||||
```
|
||||
|
||||
### 3. 启动 Worker
|
||||
|
||||
```bash
|
||||
go run cmd/worker/main.go
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 轮询配置(tb_polling_config)
|
||||
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| name | 配置名称 | - |
|
||||
| priority | 优先级(数字越大越优先) | 0 |
|
||||
| carrier_id | 运营商 ID(可选) | - |
|
||||
| status | 卡状态条件(可选) | - |
|
||||
| card_category | 卡类别条件(可选) | - |
|
||||
| realname_check_interval | 实名检查间隔(秒) | 3600 |
|
||||
| carddata_check_interval | 流量检查间隔(秒) | 7200 |
|
||||
| package_check_interval | 套餐检查间隔(秒) | 14400 |
|
||||
| is_enabled | 是否启用 | true |
|
||||
|
||||
### 并发控制配置(tb_polling_concurrency_config)
|
||||
|
||||
| 任务类型 | 默认并发数 | 说明 |
|
||||
|----------|-----------|------|
|
||||
| realname | 50 | 实名检查并发数 |
|
||||
| carddata | 100 | 流量检查并发数 |
|
||||
| package | 30 | 套餐检查并发数 |
|
||||
|
||||
## API 接口
|
||||
|
||||
### 轮询配置管理
|
||||
|
||||
- `POST /api/admin/polling-configs` - 创建配置
|
||||
- `GET /api/admin/polling-configs` - 配置列表
|
||||
- `GET /api/admin/polling-configs/:id` - 配置详情
|
||||
- `PUT /api/admin/polling-configs/:id` - 更新配置
|
||||
- `DELETE /api/admin/polling-configs/:id` - 删除配置
|
||||
|
||||
### 并发控制管理
|
||||
|
||||
- `GET /api/admin/polling-concurrency` - 获取并发配置
|
||||
- `PUT /api/admin/polling-concurrency/:type` - 更新并发数
|
||||
- `POST /api/admin/polling-concurrency/reset` - 重置信号量
|
||||
|
||||
### 监控面板
|
||||
|
||||
- `GET /api/admin/polling-stats` - 总览统计
|
||||
- `GET /api/admin/polling-stats/queues` - 队列状态
|
||||
- `GET /api/admin/polling-stats/tasks` - 任务统计
|
||||
- `GET /api/admin/polling-stats/init-progress` - 初始化进度
|
||||
|
||||
### 告警管理
|
||||
|
||||
- `POST /api/admin/polling-alert-rules` - 创建告警规则
|
||||
- `GET /api/admin/polling-alert-rules` - 规则列表
|
||||
- `PUT /api/admin/polling-alert-rules/:id` - 更新规则
|
||||
- `DELETE /api/admin/polling-alert-rules/:id` - 删除规则
|
||||
- `GET /api/admin/polling-alert-history` - 告警历史
|
||||
|
||||
### 数据清理
|
||||
|
||||
- `POST /api/admin/data-cleanup-configs` - 创建清理配置
|
||||
- `GET /api/admin/data-cleanup-configs` - 配置列表
|
||||
- `PUT /api/admin/data-cleanup-configs/:id` - 更新配置
|
||||
- `DELETE /api/admin/data-cleanup-configs/:id` - 删除配置
|
||||
- `POST /api/admin/data-cleanup/trigger` - 手动触发清理
|
||||
- `GET /api/admin/data-cleanup/preview` - 清理预览
|
||||
- `GET /api/admin/data-cleanup/progress` - 清理进度
|
||||
|
||||
### 手动触发
|
||||
|
||||
- `POST /api/admin/polling-manual-trigger/single` - 单卡触发
|
||||
- `POST /api/admin/polling-manual-trigger/batch` - 批量触发
|
||||
- `POST /api/admin/polling-manual-trigger/by-condition` - 条件触发
|
||||
- `GET /api/admin/polling-manual-trigger/status` - 触发状态
|
||||
- `GET /api/admin/polling-manual-trigger/history` - 触发历史
|
||||
- `POST /api/admin/polling-manual-trigger/cancel` - 取消触发
|
||||
|
||||
## Redis Key 说明
|
||||
|
||||
| Key 模式 | 类型 | 说明 |
|
||||
|----------|------|------|
|
||||
| polling:queue:realname | Sorted Set | 实名检查队列 |
|
||||
| polling:queue:carddata | Sorted Set | 流量检查队列 |
|
||||
| polling:queue:package | Sorted Set | 套餐检查队列 |
|
||||
| polling:manual:{type} | List | 手动触发队列 |
|
||||
| polling:card:{card_id} | Hash | 卡信息缓存 |
|
||||
| polling:configs | Hash | 配置缓存 |
|
||||
| polling:concurrency:config:{type} | String | 并发配置 |
|
||||
| polling:concurrency:current:{type} | String | 当前并发数 |
|
||||
| polling:stats:{type} | Hash | 监控统计 |
|
||||
| polling:init:progress | Hash | 初始化进度 |
|
||||
|
||||
## 性能指标
|
||||
|
||||
- Worker 启动时间:< 10 秒
|
||||
- 渐进式初始化:每批 10 万张卡,间隔 1 秒
|
||||
- API 响应时间:P95 < 200ms
|
||||
- 数据库查询:< 50ms
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [部署文档](deployment.md)
|
||||
- [运维文档](operations.md)
|
||||
213
docs/polling-system/deployment.md
Normal file
213
docs/polling-system/deployment.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# 轮询系统部署文档
|
||||
|
||||
## 部署前准备
|
||||
|
||||
### 1. 环境要求
|
||||
|
||||
| 组件 | 最低版本 | 推荐版本 |
|
||||
|------|----------|----------|
|
||||
| PostgreSQL | 14.0 | 14+ |
|
||||
| Redis | 6.0 | 6.0+ |
|
||||
| Go | 1.21 | 1.21+ |
|
||||
|
||||
### 2. 配置检查
|
||||
|
||||
确保以下环境变量已配置:
|
||||
|
||||
```bash
|
||||
# 数据库配置
|
||||
JUNHONG_DATABASE_HOST
|
||||
JUNHONG_DATABASE_PORT
|
||||
JUNHONG_DATABASE_USER
|
||||
JUNHONG_DATABASE_PASSWORD
|
||||
JUNHONG_DATABASE_DBNAME
|
||||
|
||||
# Redis 配置
|
||||
JUNHONG_REDIS_ADDRESS
|
||||
JUNHONG_REDIS_PORT
|
||||
JUNHONG_REDIS_PASSWORD
|
||||
JUNHONG_REDIS_DB
|
||||
```
|
||||
|
||||
## 部署步骤
|
||||
|
||||
### 步骤 1: 数据库迁移
|
||||
|
||||
```bash
|
||||
# 检查迁移状态
|
||||
make migrate-status
|
||||
|
||||
# 执行迁移
|
||||
make migrate-up
|
||||
|
||||
# 验证迁移结果
|
||||
psql $DATABASE_URL -c "SELECT tablename FROM pg_tables WHERE tablename LIKE 'tb_polling%' OR tablename LIKE 'tb_data_%';"
|
||||
```
|
||||
|
||||
应该看到以下表:
|
||||
- tb_polling_config
|
||||
- tb_polling_concurrency_config
|
||||
- tb_polling_alert_rule
|
||||
- tb_polling_alert_history
|
||||
- tb_data_cleanup_config
|
||||
- tb_data_cleanup_log
|
||||
- tb_polling_manual_trigger_log
|
||||
- tb_data_usage_record
|
||||
|
||||
### 步骤 2: 初始化配置
|
||||
|
||||
```bash
|
||||
# 执行初始化脚本
|
||||
psql $DATABASE_URL -f scripts/init_polling_config.sql
|
||||
|
||||
# 验证初始化结果
|
||||
psql $DATABASE_URL -c "SELECT config_name, priority, status FROM tb_polling_config ORDER BY priority;"
|
||||
```
|
||||
|
||||
应该看到 5 条默认配置:
|
||||
1. 未实名卡轮询 (priority: 10)
|
||||
2. 行业卡轮询 (priority: 15)
|
||||
3. 已实名卡轮询 (priority: 20)
|
||||
4. 已激活卡轮询 (priority: 30)
|
||||
5. 默认轮询配置 (priority: 100)
|
||||
|
||||
### 步骤 3: 验证 Redis 连接
|
||||
|
||||
```bash
|
||||
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD ping
|
||||
```
|
||||
|
||||
### 步骤 4: 编译应用
|
||||
|
||||
```bash
|
||||
# 编译 API 服务
|
||||
go build -o bin/api cmd/api/main.go
|
||||
|
||||
# 编译 Worker 服务
|
||||
go build -o bin/worker cmd/worker/main.go
|
||||
```
|
||||
|
||||
### 步骤 5: 灰度发布
|
||||
|
||||
**阶段 1: 单节点测试**
|
||||
|
||||
1. 先在一台 Worker 上部署新版本
|
||||
2. 观察日志和监控指标 30 分钟
|
||||
3. 确认无异常后继续
|
||||
|
||||
```bash
|
||||
# 启动单个 Worker
|
||||
./bin/worker &
|
||||
|
||||
# 检查日志
|
||||
tail -f logs/worker.log | grep -i polling
|
||||
```
|
||||
|
||||
**阶段 2: 滚动部署**
|
||||
|
||||
1. 逐步替换其他 Worker 节点
|
||||
2. 每个节点间隔 5 分钟
|
||||
3. 持续监控告警
|
||||
|
||||
### 步骤 6: 验证部署
|
||||
|
||||
```bash
|
||||
# 检查调度器状态
|
||||
curl http://localhost:3000/api/admin/polling-stats/init-progress
|
||||
|
||||
# 检查队列状态
|
||||
curl http://localhost:3000/api/admin/polling-stats/queues
|
||||
|
||||
# 检查配置列表
|
||||
curl http://localhost:3000/api/admin/polling-configs
|
||||
```
|
||||
|
||||
## 配置调整
|
||||
|
||||
### 调整并发数
|
||||
|
||||
```bash
|
||||
# 查看当前并发配置
|
||||
curl http://localhost:3000/api/admin/polling-concurrency
|
||||
|
||||
# 调整实名检查并发数为 80
|
||||
curl -X PUT http://localhost:3000/api/admin/polling-concurrency/realname \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"max_concurrency": 80}'
|
||||
```
|
||||
|
||||
### 调整轮询间隔
|
||||
|
||||
通过管理后台或 API 修改 tb_polling_config 表中的间隔配置。
|
||||
|
||||
## 回滚策略
|
||||
|
||||
### 快速回滚
|
||||
|
||||
1. 停止所有 Worker
|
||||
2. 回滚代码版本
|
||||
3. 执行数据库回滚(如需)
|
||||
4. 重启 Worker
|
||||
|
||||
```bash
|
||||
# 停止 Worker
|
||||
pkill -f "bin/worker"
|
||||
|
||||
# 数据库回滚(如需)
|
||||
make migrate-down STEP=9
|
||||
|
||||
# 清理 Redis 轮询相关数据
|
||||
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD --scan --pattern "polling:*" | xargs redis-cli DEL
|
||||
|
||||
# 重新部署旧版本
|
||||
./bin/worker-old &
|
||||
```
|
||||
|
||||
### 数据清理
|
||||
|
||||
如果需要完全清理轮询系统数据:
|
||||
|
||||
```sql
|
||||
-- 清理轮询配置
|
||||
TRUNCATE TABLE tb_polling_config CASCADE;
|
||||
TRUNCATE TABLE tb_polling_concurrency_config CASCADE;
|
||||
TRUNCATE TABLE tb_polling_alert_rule CASCADE;
|
||||
TRUNCATE TABLE tb_polling_alert_history CASCADE;
|
||||
TRUNCATE TABLE tb_data_cleanup_config CASCADE;
|
||||
TRUNCATE TABLE tb_data_cleanup_log CASCADE;
|
||||
TRUNCATE TABLE tb_polling_manual_trigger_log CASCADE;
|
||||
TRUNCATE TABLE tb_data_usage_record CASCADE;
|
||||
```
|
||||
|
||||
```bash
|
||||
# 清理 Redis
|
||||
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD KEYS "polling:*" | xargs redis-cli DEL
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: Worker 启动缓慢
|
||||
|
||||
A: 检查数据库和 Redis 连接。正常情况下 Worker 应在 10 秒内完成启动。
|
||||
|
||||
### Q2: 队列积压严重
|
||||
|
||||
A: 增加并发数,或检查是否有 Gateway 接口响应慢的问题。
|
||||
|
||||
### Q3: 任务重复执行
|
||||
|
||||
A: 检查 Redis 连接稳定性,确保分布式锁正常工作。
|
||||
|
||||
### Q4: 迁移失败
|
||||
|
||||
A: 检查迁移日志,确认数据库权限。可能需要手动修复 schema_migrations 表。
|
||||
|
||||
## 监控建议
|
||||
|
||||
部署后建议配置以下监控:
|
||||
|
||||
1. **队列长度**:polling:queue:* 的 ZCARD
|
||||
2. **任务成功率**:统计 success/total 比率
|
||||
3. **平均耗时**:关注 P95 和 P99
|
||||
4. **并发使用率**:current/max 比率
|
||||
5. **告警触发数**:tb_polling_alert_history 新增数
|
||||
361
docs/polling-system/operations.md
Normal file
361
docs/polling-system/operations.md
Normal file
@@ -0,0 +1,361 @@
|
||||
# 轮询系统运维文档
|
||||
|
||||
## 日常监控
|
||||
|
||||
### 1. 监控面板
|
||||
|
||||
访问监控接口获取系统状态:
|
||||
|
||||
```bash
|
||||
# 总览统计
|
||||
curl http://localhost:3000/api/admin/polling-stats
|
||||
|
||||
# 队列状态
|
||||
curl http://localhost:3000/api/admin/polling-stats/queues
|
||||
|
||||
# 任务统计
|
||||
curl http://localhost:3000/api/admin/polling-stats/tasks
|
||||
|
||||
# 初始化进度
|
||||
curl http://localhost:3000/api/admin/polling-stats/init-progress
|
||||
```
|
||||
|
||||
### 2. 关键指标
|
||||
|
||||
| 指标 | 正常范围 | 告警阈值 | 说明 |
|
||||
|------|----------|----------|------|
|
||||
| 队列长度 | < 10000 | > 50000 | 队列积压严重需关注 |
|
||||
| 成功率 | > 95% | < 90% | 任务执行成功率 |
|
||||
| 平均耗时 | < 500ms | > 2000ms | 单任务处理时间 |
|
||||
| 并发使用率 | 50-80% | > 95% | 接近上限需扩容 |
|
||||
|
||||
### 3. Redis 监控命令
|
||||
|
||||
```bash
|
||||
# 查看队列长度
|
||||
redis-cli ZCARD polling:queue:realname
|
||||
redis-cli ZCARD polling:queue:carddata
|
||||
redis-cli ZCARD polling:queue:package
|
||||
|
||||
# 查看手动触发队列
|
||||
redis-cli LLEN polling:manual:realname
|
||||
redis-cli LLEN polling:manual:carddata
|
||||
redis-cli LLEN polling:manual:package
|
||||
|
||||
# 查看当前并发数
|
||||
redis-cli GET polling:concurrency:current:realname
|
||||
redis-cli GET polling:concurrency:current:carddata
|
||||
redis-cli GET polling:concurrency:current:package
|
||||
|
||||
# 查看统计数据
|
||||
redis-cli HGETALL polling:stats:realname
|
||||
redis-cli HGETALL polling:stats:carddata
|
||||
redis-cli HGETALL polling:stats:package
|
||||
|
||||
# 查看初始化进度
|
||||
redis-cli HGETALL polling:init:progress
|
||||
```
|
||||
|
||||
## 告警配置
|
||||
|
||||
### 1. 默认告警规则
|
||||
|
||||
建议配置以下告警规则:
|
||||
|
||||
```bash
|
||||
# 队列积压告警
|
||||
curl -X POST http://localhost:3000/api/admin/polling-alert-rules \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "队列积压告警",
|
||||
"rule_type": "queue_backlog",
|
||||
"task_type": "realname",
|
||||
"threshold": 50000,
|
||||
"comparison": ">",
|
||||
"is_enabled": true,
|
||||
"notify_channels": ["webhook"],
|
||||
"webhook_url": "https://your-webhook-url"
|
||||
}'
|
||||
|
||||
# 成功率告警
|
||||
curl -X POST http://localhost:3000/api/admin/polling-alert-rules \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "成功率告警",
|
||||
"rule_type": "success_rate",
|
||||
"task_type": "realname",
|
||||
"threshold": 90,
|
||||
"comparison": "<",
|
||||
"is_enabled": true,
|
||||
"notify_channels": ["webhook"],
|
||||
"webhook_url": "https://your-webhook-url"
|
||||
}'
|
||||
|
||||
# 平均耗时告警
|
||||
curl -X POST http://localhost:3000/api/admin/polling-alert-rules \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "耗时告警",
|
||||
"rule_type": "avg_duration",
|
||||
"task_type": "realname",
|
||||
"threshold": 2000,
|
||||
"comparison": ">",
|
||||
"is_enabled": true,
|
||||
"notify_channels": ["webhook"],
|
||||
"webhook_url": "https://your-webhook-url"
|
||||
}'
|
||||
```
|
||||
|
||||
### 2. 告警历史查询
|
||||
|
||||
```bash
|
||||
# 查看告警历史
|
||||
curl "http://localhost:3000/api/admin/polling-alert-history?page=1&page_size=20"
|
||||
|
||||
# 按规则筛选
|
||||
curl "http://localhost:3000/api/admin/polling-alert-history?rule_id=1"
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题 1: 队列积压
|
||||
|
||||
**现象**: 队列长度持续增长,任务处理速度跟不上
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
1. 检查并发使用情况
|
||||
```bash
|
||||
redis-cli GET polling:concurrency:current:realname
|
||||
redis-cli GET polling:concurrency:config:realname
|
||||
```
|
||||
|
||||
2. 检查 Gateway 接口响应时间
|
||||
```bash
|
||||
# 查看统计中的平均耗时
|
||||
redis-cli HGET polling:stats:realname avg_duration_ms
|
||||
```
|
||||
|
||||
3. 检查是否有大量失败重试
|
||||
```bash
|
||||
redis-cli HGET polling:stats:realname failed
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 增加并发数
|
||||
```bash
|
||||
curl -X PUT http://localhost:3000/api/admin/polling-concurrency/realname \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"max_concurrency": 100}'
|
||||
```
|
||||
|
||||
2. 临时禁用非关键配置
|
||||
```bash
|
||||
curl -X PUT http://localhost:3000/api/admin/polling-configs/1 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": 0}'
|
||||
```
|
||||
|
||||
### 问题 2: 任务执行失败率高
|
||||
|
||||
**现象**: 成功率低于 90%
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
1. 查看 Worker 日志
|
||||
```bash
|
||||
grep -i "error" logs/worker.log | tail -100
|
||||
```
|
||||
|
||||
2. 检查 Gateway 服务状态
|
||||
3. 检查网络连接
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 如果是 Gateway 问题,联系运营商解决
|
||||
2. 如果是网络问题,检查防火墙和 DNS 配置
|
||||
3. 临时降低并发数,减少压力
|
||||
|
||||
### 问题 3: 初始化卡住
|
||||
|
||||
**现象**: 初始化进度长时间不变
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
1. 检查初始化进度
|
||||
```bash
|
||||
redis-cli HGETALL polling:init:progress
|
||||
```
|
||||
|
||||
2. 查看 Worker 日志是否有错误
|
||||
```bash
|
||||
grep -i "初始化" logs/worker.log | tail -50
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 重启 Worker 服务
|
||||
2. 如果持续失败,检查数据库连接
|
||||
|
||||
### 问题 4: 并发信号量泄漏
|
||||
|
||||
**现象**: 当前并发数异常高,但实际没有那么多任务在运行
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
```bash
|
||||
# 检查当前并发数
|
||||
redis-cli GET polling:concurrency:current:realname
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
|
||||
重置信号量:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/admin/polling-concurrency/reset \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"task_type": "realname"}'
|
||||
```
|
||||
|
||||
## 数据清理
|
||||
|
||||
### 1. 查看清理配置
|
||||
|
||||
```bash
|
||||
curl http://localhost:3000/api/admin/data-cleanup-configs
|
||||
```
|
||||
|
||||
### 2. 手动触发清理
|
||||
|
||||
```bash
|
||||
# 预览清理范围
|
||||
curl http://localhost:3000/api/admin/data-cleanup/preview
|
||||
|
||||
# 手动触发清理
|
||||
curl -X POST http://localhost:3000/api/admin/data-cleanup/trigger
|
||||
|
||||
# 查看清理进度
|
||||
curl http://localhost:3000/api/admin/data-cleanup/progress
|
||||
```
|
||||
|
||||
### 3. 调整保留天数
|
||||
|
||||
```bash
|
||||
curl -X PUT http://localhost:3000/api/admin/data-cleanup-configs/1 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"retention_days": 60}'
|
||||
```
|
||||
|
||||
## 手动触发操作
|
||||
|
||||
### 1. 单卡触发
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/admin/polling-manual-trigger/single \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"card_id": 12345,
|
||||
"task_type": "realname"
|
||||
}'
|
||||
```
|
||||
|
||||
### 2. 批量触发
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/admin/polling-manual-trigger/batch \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"card_ids": [12345, 12346, 12347],
|
||||
"task_type": "carddata"
|
||||
}'
|
||||
```
|
||||
|
||||
### 3. 条件触发
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/admin/polling-manual-trigger/by-condition \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"task_type": "realname",
|
||||
"carrier_id": 1,
|
||||
"status": 1
|
||||
}'
|
||||
```
|
||||
|
||||
### 4. 取消触发
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/admin/polling-manual-trigger/cancel \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"trigger_id": "xxx"
|
||||
}'
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 并发数调优
|
||||
|
||||
根据 Gateway 接口响应时间和服务器资源调整并发数:
|
||||
|
||||
| 场景 | 建议并发数 |
|
||||
|------|-----------|
|
||||
| Gateway 响应 < 100ms | 100-200 |
|
||||
| Gateway 响应 100-500ms | 50-100 |
|
||||
| Gateway 响应 > 500ms | 20-50 |
|
||||
|
||||
### 2. 轮询间隔调优
|
||||
|
||||
根据业务需求调整间隔:
|
||||
|
||||
| 任务类型 | 建议间隔 | 说明 |
|
||||
|----------|----------|------|
|
||||
| 实名检查(未实名) | 60s | 需要快速获知实名状态 |
|
||||
| 实名检查(已实名) | 3600s | 状态稳定,低频检查 |
|
||||
| 流量检查 | 1800s | 30分钟一次 |
|
||||
| 套餐检查 | 1800s | 与流量检查同步 |
|
||||
|
||||
### 3. 批量处理优化
|
||||
|
||||
- 渐进式初始化:每批 10 万张卡,间隔 1 秒
|
||||
- 数据清理:每批 10000 条,避免长事务
|
||||
|
||||
## 备份与恢复
|
||||
|
||||
### 1. 配置备份
|
||||
|
||||
```bash
|
||||
# 备份轮询配置
|
||||
pg_dump -h $HOST -U $USER -d $DB -t tb_polling_config > polling_config_backup.sql
|
||||
pg_dump -h $HOST -U $USER -d $DB -t tb_polling_concurrency_config > concurrency_config_backup.sql
|
||||
pg_dump -h $HOST -U $USER -d $DB -t tb_polling_alert_rule > alert_rules_backup.sql
|
||||
pg_dump -h $HOST -U $USER -d $DB -t tb_data_cleanup_config > cleanup_config_backup.sql
|
||||
```
|
||||
|
||||
### 2. 恢复配置
|
||||
|
||||
```bash
|
||||
psql -h $HOST -U $USER -d $DB < polling_config_backup.sql
|
||||
```
|
||||
|
||||
## 日志说明
|
||||
|
||||
### 日志位置
|
||||
|
||||
- Worker 日志:`logs/worker.log`
|
||||
- API 日志:`logs/api.log`
|
||||
- 访问日志:`logs/access.log`
|
||||
|
||||
### 关键日志关键词
|
||||
|
||||
| 关键词 | 含义 |
|
||||
|--------|------|
|
||||
| `轮询调度器启动` | Worker 启动成功 |
|
||||
| `渐进式初始化` | 初始化进行中 |
|
||||
| `实名检查完成` | 实名检查任务完成 |
|
||||
| `流量检查完成` | 流量检查任务完成 |
|
||||
| `套餐检查完成` | 套餐检查任务完成 |
|
||||
| `告警触发` | 告警规则触发 |
|
||||
| `数据清理完成` | 清理任务完成 |
|
||||
165
docs/polling-system/performance-tuning.md
Normal file
165
docs/polling-system/performance-tuning.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# 轮询系统性能调优指南
|
||||
|
||||
## 千万卡规模优化方案
|
||||
|
||||
### 1. 调度器优化
|
||||
|
||||
当前配置存在瓶颈:每次只取 1000 张卡,每 10 秒调度一次,每分钟最多处理 6000 张卡。
|
||||
|
||||
**优化方案**:
|
||||
|
||||
```go
|
||||
// 修改 scheduler.go 中的 processTimedQueue
|
||||
cardIDs, err := s.redis.ZRangeByScore(ctx, queueKey, &redis.ZRangeBy{
|
||||
Min: "-inf",
|
||||
Max: formatInt64(now),
|
||||
Count: 10000, // 从 1000 提高到 10000
|
||||
}).Result()
|
||||
```
|
||||
|
||||
调整调度间隔:
|
||||
```go
|
||||
func DefaultSchedulerConfig() *SchedulerConfig {
|
||||
return &SchedulerConfig{
|
||||
ScheduleInterval: 5 * time.Second, // 从 10 秒改为 5 秒
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
优化后:每分钟可处理 12 万张卡
|
||||
|
||||
### 2. 并发控制优化
|
||||
|
||||
修改 `scripts/init_polling_config.sql`:
|
||||
|
||||
```sql
|
||||
-- 千万卡规模的并发配置
|
||||
INSERT INTO tb_polling_concurrency_config (task_type, max_concurrency, description) VALUES
|
||||
('realname', 500, '实名检查并发数'),
|
||||
('carddata', 1000, '流量检查并发数'),
|
||||
('package', 500, '套餐检查并发数'),
|
||||
('stop_start', 100, '停复机操作并发数');
|
||||
```
|
||||
|
||||
### 3. Worker 多实例部署
|
||||
|
||||
部署多个 Worker 实例分担负载:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml 示例
|
||||
services:
|
||||
worker-1:
|
||||
image: junhong-cmp-worker
|
||||
environment:
|
||||
- WORKER_ID=1
|
||||
worker-2:
|
||||
image: junhong-cmp-worker
|
||||
environment:
|
||||
- WORKER_ID=2
|
||||
worker-3:
|
||||
image: junhong-cmp-worker
|
||||
environment:
|
||||
- WORKER_ID=3
|
||||
```
|
||||
|
||||
注意:只需一个实例运行调度器,其他实例只处理任务。
|
||||
|
||||
### 4. 检查间隔优化
|
||||
|
||||
根据业务需求调整检查间隔,减少不必要的检查:
|
||||
|
||||
| 卡状态 | 当前间隔 | 建议间隔 | 说明 |
|
||||
|--------|---------|---------|------|
|
||||
| 未实名 | 60 秒 | 300 秒 | 实名状态不会频繁变化 |
|
||||
| 已实名 | 3600 秒 | 86400 秒 | 已实名只需每天检查一次 |
|
||||
| 已激活流量 | 1800 秒 | 3600 秒 | 每小时检查一次足够 |
|
||||
|
||||
这样可以大幅减少检查次数:
|
||||
- 原方案:1000 万次/小时
|
||||
- 优化后:约 100 万次/小时
|
||||
|
||||
### 5. 初始化优化
|
||||
|
||||
使用 Pipeline 批量写入 Redis:
|
||||
|
||||
```go
|
||||
// 优化 initCardPolling,使用 Pipeline
|
||||
func (s *Scheduler) initCardsBatch(ctx context.Context, cards []*model.IotCard) error {
|
||||
pipe := s.redis.Pipeline()
|
||||
for _, card := range cards {
|
||||
config := s.MatchConfig(card)
|
||||
if config == nil {
|
||||
continue
|
||||
}
|
||||
// 批量 ZADD
|
||||
nextTime := s.calculateNextCheckTime(card, config)
|
||||
pipe.ZAdd(ctx, queueKey, redis.Z{Score: float64(nextTime), Member: card.ID})
|
||||
// 批量 HSET
|
||||
pipe.HSet(ctx, cacheKey, cardData)
|
||||
}
|
||||
_, err := pipe.Exec(ctx)
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
优化效果:减少 Redis 往返次数,初始化时间从 150 秒降至 30-50 秒
|
||||
|
||||
### 6. 数据库索引优化
|
||||
|
||||
确保以下索引存在:
|
||||
|
||||
```sql
|
||||
-- 用于渐进式初始化的游标分页
|
||||
CREATE INDEX IF NOT EXISTS idx_iot_card_id_asc ON tb_iot_card(id ASC) WHERE deleted_at IS NULL;
|
||||
|
||||
-- 用于条件筛选
|
||||
CREATE INDEX IF NOT EXISTS idx_iot_card_polling
|
||||
ON tb_iot_card(enable_polling, real_name_status, activation_status, card_category);
|
||||
```
|
||||
|
||||
### 7. Redis 配置优化
|
||||
|
||||
```conf
|
||||
# redis.conf
|
||||
maxmemory 8gb
|
||||
maxmemory-policy allkeys-lru
|
||||
|
||||
# 连接池优化
|
||||
tcp-keepalive 300
|
||||
timeout 0
|
||||
```
|
||||
|
||||
### 8. 监控告警阈值
|
||||
|
||||
千万卡规模的告警阈值建议:
|
||||
|
||||
```sql
|
||||
INSERT INTO tb_polling_alert_rule (rule_name, metric_type, task_type, threshold, comparison, alert_level) VALUES
|
||||
('队列积压告警', 'queue_size', 'polling:realname', 100000, 'gt', 'critical'),
|
||||
('失败率告警', 'failure_rate', 'polling:realname', 10, 'gt', 'warning'),
|
||||
('延迟告警', 'avg_wait_time', 'polling:carddata', 3600, 'gt', 'warning');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 容量规划
|
||||
|
||||
| 规模 | Worker 数 | Redis 内存 | 并发总数 | 预估 QPS |
|
||||
|------|----------|-----------|---------|---------|
|
||||
| 100 万卡 | 2 | 512MB | 200 | 1000 |
|
||||
| 500 万卡 | 4 | 2GB | 500 | 3000 |
|
||||
| 1000 万卡 | 8 | 4GB | 1000 | 5000 |
|
||||
| 2000 万卡 | 16 | 8GB | 2000 | 10000 |
|
||||
|
||||
## 压测建议
|
||||
|
||||
1. 使用 `wrk` 或 `vegeta` 对 API 进行压测
|
||||
2. 使用脚本批量创建测试卡验证初始化性能
|
||||
3. 监控 Redis 内存和 CPU 使用率
|
||||
4. 监控数据库连接池和查询延迟
|
||||
|
||||
```bash
|
||||
# API 压测示例
|
||||
wrk -t12 -c400 -d30s http://localhost:3000/api/admin/polling-stats
|
||||
```
|
||||
Reference in New Issue
Block a user