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:
107
internal/polling/api_callback.go
Normal file
107
internal/polling/api_callback.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package polling
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// APICallback API 进程使用的轻量级轮询回调
|
||||
// 直接操作 Redis 队列,不依赖调度器
|
||||
type APICallback struct {
|
||||
redis *redis.Client
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewAPICallback 创建 API 回调实例
|
||||
func NewAPICallback(redis *redis.Client, logger *zap.Logger) *APICallback {
|
||||
return &APICallback{
|
||||
redis: redis,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// OnCardCreated 卡创建时的回调
|
||||
// 注意:大多数卡创建是通过 Worker 的批量导入完成的,这个方法主要用于单卡创建场景
|
||||
func (c *APICallback) OnCardCreated(ctx context.Context, card *model.IotCard) {
|
||||
if card == nil {
|
||||
return
|
||||
}
|
||||
c.logger.Debug("API 回调:卡创建", zap.Uint("card_id", card.ID))
|
||||
// 卡创建后,scheduler 的渐进式初始化会将其加入队列
|
||||
// 这里不做处理,让 scheduler 处理
|
||||
}
|
||||
|
||||
// OnCardStatusChanged 卡状态变化时的回调
|
||||
func (c *APICallback) OnCardStatusChanged(ctx context.Context, cardID uint) {
|
||||
c.logger.Debug("API 回调:卡状态变化", zap.Uint("card_id", cardID))
|
||||
// 状态变化后,scheduler 下次扫描时会更新配置匹配
|
||||
// 这里不做处理,让 scheduler 处理
|
||||
}
|
||||
|
||||
// OnCardDeleted 卡删除时的回调
|
||||
// 从所有队列中移除卡
|
||||
func (c *APICallback) OnCardDeleted(ctx context.Context, cardID uint) {
|
||||
c.logger.Debug("API 回调:卡删除", zap.Uint("card_id", cardID))
|
||||
|
||||
member := strconv.FormatUint(uint64(cardID), 10)
|
||||
|
||||
// 从所有轮询队列中移除
|
||||
queues := []string{
|
||||
constants.RedisPollingQueueRealnameKey(),
|
||||
constants.RedisPollingQueueCarddataKey(),
|
||||
constants.RedisPollingQueuePackageKey(),
|
||||
}
|
||||
|
||||
for _, queueKey := range queues {
|
||||
if err := c.redis.ZRem(ctx, queueKey, member).Err(); err != nil {
|
||||
c.logger.Warn("从队列移除卡失败",
|
||||
zap.String("queue", queueKey),
|
||||
zap.Uint("card_id", cardID),
|
||||
zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// 删除卡信息缓存
|
||||
cacheKey := constants.RedisPollingCardInfoKey(cardID)
|
||||
if err := c.redis.Del(ctx, cacheKey).Err(); err != nil {
|
||||
c.logger.Warn("删除卡缓存失败",
|
||||
zap.Uint("card_id", cardID),
|
||||
zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// OnCardEnabled 卡启用轮询时的回调
|
||||
func (c *APICallback) OnCardEnabled(ctx context.Context, cardID uint) {
|
||||
c.logger.Debug("API 回调:卡启用轮询", zap.Uint("card_id", cardID))
|
||||
// 启用后,scheduler 下次扫描时会将其加入队列
|
||||
}
|
||||
|
||||
// OnCardDisabled 卡禁用轮询时的回调
|
||||
// 从所有队列中移除卡
|
||||
func (c *APICallback) OnCardDisabled(ctx context.Context, cardID uint) {
|
||||
c.logger.Debug("API 回调:卡禁用轮询", zap.Uint("card_id", cardID))
|
||||
|
||||
member := strconv.FormatUint(uint64(cardID), 10)
|
||||
|
||||
// 从所有轮询队列中移除
|
||||
queues := []string{
|
||||
constants.RedisPollingQueueRealnameKey(),
|
||||
constants.RedisPollingQueueCarddataKey(),
|
||||
constants.RedisPollingQueuePackageKey(),
|
||||
}
|
||||
|
||||
for _, queueKey := range queues {
|
||||
if err := c.redis.ZRem(ctx, queueKey, member).Err(); err != nil {
|
||||
c.logger.Warn("从队列移除卡失败",
|
||||
zap.String("queue", queueKey),
|
||||
zap.Uint("card_id", cardID),
|
||||
zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user