All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m24s
- 开关开启但分发器心跳缺失时回退为当场执行,避免指令无人消费 - 待确认指令先查上游状态再判断;执行期间被覆盖时把待确认带到新指令 - 同方向失败退避中重复写入不提前执行时间 - 领取容量把执行上限与本秒剩余 QPS 分开计算 - 队列复机共用单卡复机锁;执行上下文取消后按结果未知确认 - 消费者并行停止;读取最近调用时间失败时不覆盖;成功但终结日志失败按成功处理 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
73 lines
2.2 KiB
Go
73 lines
2.2 KiB
Go
package carrierqueue
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
)
|
|
|
|
// heartbeatFreshness 是分发器心跳有效期:超过该时长没有心跳视为指令无人消费。
|
|
const heartbeatFreshness = 30 * time.Second
|
|
|
|
// heartbeatCacheTTL 是进程内缓存心跳判断的时长,避免每次请求评估都读 Redis。
|
|
const heartbeatCacheTTL = 3 * time.Second
|
|
|
|
// CommandQueue 是停复机指令队列写入端:开关读取受控系统配置,写入走共享缓存 Lua。
|
|
type CommandQueue struct {
|
|
store *Store
|
|
settings *SettingsProvider
|
|
|
|
mu sync.Mutex
|
|
aliveAt time.Time
|
|
alive bool
|
|
checkedAt time.Time
|
|
}
|
|
|
|
// NewCommandQueue 创建停复机指令队列写入端。
|
|
func NewCommandQueue(store *Store, settings *SettingsProvider) *CommandQueue {
|
|
return &CommandQueue{store: store, settings: settings}
|
|
}
|
|
|
|
// Enabled 返回是否经指令队列执行:停复机出口开关开启,且分发器在心跳有效期内运行(有消费方)。
|
|
// 开关开启但没有分发器时回退为各入口当场执行,避免指令写入后无人消费。
|
|
func (q *CommandQueue) Enabled(ctx context.Context) bool {
|
|
if q == nil || q.store == nil || !q.settings.Current(ctx).StopResumeQueueEnabled {
|
|
return false
|
|
}
|
|
return q.dispatcherAlive(ctx)
|
|
}
|
|
|
|
func (q *CommandQueue) dispatcherAlive(ctx context.Context) bool {
|
|
q.mu.Lock()
|
|
defer q.mu.Unlock()
|
|
if time.Since(q.checkedAt) < heartbeatCacheTTL {
|
|
return q.alive
|
|
}
|
|
q.checkedAt = time.Now()
|
|
beats, err := q.store.redis.HGetAll(ctx, constants.RedisCarrierSchedulerHeartbeatKey).Result()
|
|
if err != nil {
|
|
return q.alive
|
|
}
|
|
q.alive = false
|
|
for _, raw := range beats {
|
|
if ms, convErr := strconv.ParseInt(raw, 10, 64); convErr == nil && time.Since(time.UnixMilli(ms)) < heartbeatFreshness {
|
|
q.alive = true
|
|
break
|
|
}
|
|
}
|
|
return q.alive
|
|
}
|
|
|
|
// WriteCommand 写入或覆盖卡的停复机指令。
|
|
func (q *CommandQueue) WriteCommand(ctx context.Context, cmd StopResumeCommand) (int64, error) {
|
|
return q.store.WriteCommand(ctx, cmd)
|
|
}
|
|
|
|
// RequestDeviceEval 请求一次设备维度判断。
|
|
func (q *CommandQueue) RequestDeviceEval(ctx context.Context, deviceID uint, scenes []string) error {
|
|
return q.store.RequestDeviceEval(ctx, deviceID, scenes)
|
|
}
|