Files
junhong_cmp_fiber/docs/polling-system/performance-tuning.md
huang 931e140e8e
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m35s
feat: 实现 IoT 卡轮询系统(支持千万级卡规模)
实现功能:
- 实名状态检查轮询(可配置间隔)
- 卡流量检查轮询(支持跨月流量追踪)
- 套餐检查与超额自动停机
- 分布式并发控制(Redis 信号量)
- 手动触发轮询(单卡/批量/条件筛选)
- 数据清理配置与执行
- 告警规则与历史记录
- 实时监控统计(队列/性能/并发)

性能优化:
- Redis 缓存卡信息,减少 DB 查询
- Pipeline 批量写入 Redis
- 异步流量记录写入
- 渐进式初始化(10万卡/批)

压测工具(scripts/benchmark/):
- Mock Gateway 模拟上游服务
- 测试卡生成器
- 配置初始化脚本
- 实时监控脚本

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 17:32:44 +08:00

166 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 轮询系统性能调优指南
## 千万卡规模优化方案
### 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
```