feat: 停复机指令队列与设备维度判断
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
自动停复机统一经停复机指令队列执行(全局开关 polling.stop_resume.queue_enabled,默认关闭): - 统一判断函数 decideCard,新旧路径共用;暂停轮询的卡在套餐到期、退款场景照常停机 - 同卡一条指令、版本号覆盖;领取校验单卡最小重试间隔;先于卡任务领取,不受 scheduler_mode 限制 - 执行器只调用一次网关,按错误分类退避与告警,结果未知在队列内确认 - 设备维度判断合并请求与场景,版本变化再判断,旧路径持设备锁时推后 - 套餐激活、重置、订单、流量扣减、套餐到期、退款、实名管控修改改为请求评估 - 监控增加停复机队列与近 1 小时执行计数,单卡轮询状态返回当前指令 - 迁移 000255 插入开关与告警、退避参数 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
70
internal/task/carrier_device_eval_executor.go
Normal file
70
internal/task/carrier_device_eval_executor.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/polling/carrierqueue"
|
||||
)
|
||||
|
||||
// 设备判断的推后时长:旧路径持设备锁时、判断失败时。
|
||||
const (
|
||||
deviceEvalLegacyLockDelay = 5 * time.Second
|
||||
deviceEvalFailureDelay = 30 * time.Second
|
||||
deviceEvalTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
// DeviceEvalExecutor 设备维度判断执行器:抢设备判断锁,读取最新数据为设备下各卡生成指令;锁内不调网关。
|
||||
type DeviceEvalExecutor struct {
|
||||
store *carrierqueue.Store
|
||||
service StopResumeCommandService
|
||||
instanceID string
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewDeviceEvalExecutor 创建设备判断执行器。
|
||||
func NewDeviceEvalExecutor(store *carrierqueue.Store, service StopResumeCommandService, instanceID string, logger *zap.Logger) *DeviceEvalExecutor {
|
||||
if logger == nil {
|
||||
logger = zap.NewNop()
|
||||
}
|
||||
return &DeviceEvalExecutor{store: store, service: service, instanceID: instanceID, logger: logger}
|
||||
}
|
||||
|
||||
// Handle 消费一条设备判断消息。收尾只移除本次已处理的场景;版本变大时用最新数据再判断一次。
|
||||
func (e *DeviceEvalExecutor) Handle(ctx context.Context, body []byte, _ func()) {
|
||||
var msg carrierqueue.DeviceEvalMessage
|
||||
if err := json.Unmarshal(body, &msg); err != nil || msg.DeviceID == 0 || msg.Token == "" {
|
||||
e.logger.Warn("设备判断消息无效,丢弃", zap.ByteString("body", body))
|
||||
return
|
||||
}
|
||||
got, err := e.store.AcquireDeviceEvalLock(ctx, msg.DeviceID, msg.Token, msg.Token+"|"+e.instanceID, 2*deviceEvalTimeout)
|
||||
if err != nil || got != 1 {
|
||||
return
|
||||
}
|
||||
claimed := carrierqueue.DeviceEvalClaimed{DeviceID: msg.DeviceID, Token: msg.Token, Version: msg.Version, Scenes: msg.Scenes}
|
||||
deferFor := time.Duration(0)
|
||||
if e.service.LegacyDeviceOpLocked(ctx, msg.DeviceID) {
|
||||
// 旧路径正在对该设备执行停复机:推后,不并行判断。
|
||||
deferFor = deviceEvalLegacyLockDelay
|
||||
} else {
|
||||
evalCtx, cancel := context.WithTimeout(ctx, deviceEvalTimeout)
|
||||
if evalErr := e.service.EvaluateDeviceCommands(evalCtx, msg.DeviceID, msg.Scenes); evalErr != nil {
|
||||
e.logger.Warn("设备判断失败,稍后再判断", zap.Uint("device_id", msg.DeviceID), zap.Strings("scenes", msg.Scenes), zap.Error(evalErr))
|
||||
deferFor = deviceEvalFailureDelay
|
||||
}
|
||||
cancel()
|
||||
}
|
||||
finishCtx, finishCancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)
|
||||
defer finishCancel()
|
||||
result, err := e.store.FinishDeviceEval(finishCtx, claimed, deferFor)
|
||||
if err != nil {
|
||||
e.logger.Warn("设备判断收尾失败,等待租约到期后重新领取", zap.Uint("device_id", msg.DeviceID), zap.Error(err))
|
||||
return
|
||||
}
|
||||
if result == 2 {
|
||||
e.logger.Info("设备判断期间有新请求,按最新数据再判断", zap.Uint("device_id", msg.DeviceID))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user