修复并发,暂时的,不够完整,后续还是需要重新设计,这个太乱了,傻逼AI
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m22s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m22s
This commit is contained in:
@@ -9,12 +9,14 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/bootstrap"
|
||||
"github.com/break/junhong_cmp_fiber/internal/gateway"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/polling"
|
||||
iot_card_svc "github.com/break/junhong_cmp_fiber/internal/service/iot_card"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
@@ -34,6 +36,7 @@ const (
|
||||
workerModulePollingInitializer = "polling_initializer"
|
||||
workerModulePollingScheduler = "polling_scheduler"
|
||||
workerModuleAsynqScheduler = "asynq_scheduler"
|
||||
importRescueLimit = 500 // 启动补偿单次扫描的最大导入任务数
|
||||
)
|
||||
|
||||
// workerModuleStatus 描述当前 Worker 角色下的模块启停计划。
|
||||
@@ -121,6 +124,7 @@ func runWorker(cfg *config.Config) {
|
||||
|
||||
taskHandler := createTaskHandler(runtime, appLogger)
|
||||
taskHandler.RegisterHandlers()
|
||||
rescuePendingImportTasks(ctx, runtime, appLogger)
|
||||
|
||||
appLogger.Info("Worker 服务器配置完成",
|
||||
zap.Int("concurrency", cfg.Queue.Concurrency),
|
||||
@@ -382,18 +386,36 @@ func startAsynqScheduler(cfg *config.Config, redisAddr string, appLogger *zap.Lo
|
||||
|
||||
// registerAsynqScheduleTasks 注册 Worker 入口需要的全部定时任务。
|
||||
func registerAsynqScheduleTasks(asynqScheduler *asynq.Scheduler) error {
|
||||
if _, err := asynqScheduler.Register("@every 1m", asynq.NewTask(constants.TaskTypeOrderExpire, nil)); err != nil {
|
||||
if _, err := asynqScheduler.Register("@every 1m", asynq.NewTask(
|
||||
constants.TaskTypeOrderExpire,
|
||||
nil,
|
||||
asynq.Queue(constants.QueueForTaskType(constants.TaskTypeOrderExpire)),
|
||||
)); err != nil {
|
||||
return fmt.Errorf("注册订单超时定时任务失败: %w", err)
|
||||
}
|
||||
if _, err := asynqScheduler.Register("@every 1m", asynq.NewTask(constants.TaskTypeAlertCheck, nil)); err != nil {
|
||||
if _, err := asynqScheduler.Register("@every 1m", asynq.NewTask(
|
||||
constants.TaskTypeAlertCheck,
|
||||
nil,
|
||||
asynq.Queue(constants.QueueForTaskType(constants.TaskTypeAlertCheck)),
|
||||
)); err != nil {
|
||||
return fmt.Errorf("注册告警检查定时任务失败: %w", err)
|
||||
}
|
||||
if _, err := asynqScheduler.Register("0 2 * * *", asynq.NewTask(constants.TaskTypeDataCleanup, nil)); err != nil {
|
||||
if _, err := asynqScheduler.Register("0 2 * * *", asynq.NewTask(
|
||||
constants.TaskTypeDataCleanup,
|
||||
nil,
|
||||
asynq.Queue(constants.QueueForTaskType(constants.TaskTypeDataCleanup)),
|
||||
)); err != nil {
|
||||
return fmt.Errorf("注册数据清理定时任务失败: %w", err)
|
||||
}
|
||||
if _, err := asynqScheduler.Register(
|
||||
"0 2 * * *",
|
||||
asynq.NewTask(constants.TaskTypeDailyTrafficFlush, nil, asynq.MaxRetry(3), asynq.Timeout(5*time.Minute)),
|
||||
asynq.NewTask(
|
||||
constants.TaskTypeDailyTrafficFlush,
|
||||
nil,
|
||||
asynq.MaxRetry(3),
|
||||
asynq.Timeout(5*time.Minute),
|
||||
asynq.Queue(constants.QueueForTaskType(constants.TaskTypeDailyTrafficFlush)),
|
||||
),
|
||||
); err != nil {
|
||||
return fmt.Errorf("注册每日流量落盘定时任务失败: %w", err)
|
||||
}
|
||||
@@ -417,6 +439,80 @@ func createTaskHandler(runtime *workerRuntime, appLogger *zap.Logger) *queue.Han
|
||||
)
|
||||
}
|
||||
|
||||
// rescuePendingImportTasks 将历史遗留的待处理导入任务重新投递到独立导入队列。
|
||||
func rescuePendingImportTasks(ctx context.Context, runtime *workerRuntime, appLogger *zap.Logger) {
|
||||
rescuePendingIotCardImportTasks(ctx, runtime.db, runtime.asynqClient, appLogger)
|
||||
rescuePendingDeviceImportTasks(ctx, runtime.db, runtime.asynqClient, appLogger)
|
||||
}
|
||||
|
||||
// rescuePendingIotCardImportTasks 补偿仍停留在待处理状态的 IoT 卡导入任务。
|
||||
func rescuePendingIotCardImportTasks(ctx context.Context, db *gorm.DB, asynqClient *asynq.Client, appLogger *zap.Logger) {
|
||||
var importTasks []model.IotCardImportTask
|
||||
if err := db.WithContext(ctx).
|
||||
Where("status = ?", model.ImportTaskStatusPending).
|
||||
Limit(importRescueLimit).
|
||||
Find(&importTasks).Error; err != nil {
|
||||
appLogger.Warn("扫描待补偿 IoT 卡导入任务失败", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, importTask := range importTasks {
|
||||
payload := task.IotCardImportPayload{TaskID: importTask.ID}
|
||||
enqueueImportRescueTask(ctx, asynqClient, constants.TaskTypeIotCardImport, payload, importTask.ID, appLogger)
|
||||
}
|
||||
}
|
||||
|
||||
// rescuePendingDeviceImportTasks 补偿仍停留在待处理状态的设备导入任务。
|
||||
func rescuePendingDeviceImportTasks(ctx context.Context, db *gorm.DB, asynqClient *asynq.Client, appLogger *zap.Logger) {
|
||||
var importTasks []model.DeviceImportTask
|
||||
if err := db.WithContext(ctx).
|
||||
Where("status = ?", model.ImportTaskStatusPending).
|
||||
Limit(importRescueLimit).
|
||||
Find(&importTasks).Error; err != nil {
|
||||
appLogger.Warn("扫描待补偿设备导入任务失败", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, importTask := range importTasks {
|
||||
payload := task.DeviceImportPayload{TaskID: importTask.ID}
|
||||
enqueueImportRescueTask(ctx, asynqClient, constants.TaskTypeDeviceImport, payload, importTask.ID, appLogger)
|
||||
}
|
||||
}
|
||||
|
||||
// enqueueImportRescueTask 将补偿任务提交到任务类型对应的独立队列。
|
||||
func enqueueImportRescueTask(ctx context.Context, asynqClient *asynq.Client, taskType string, payload any, taskID uint, appLogger *zap.Logger) {
|
||||
payloadBytes, err := sonic.Marshal(payload)
|
||||
if err != nil {
|
||||
appLogger.Warn("序列化导入补偿任务载荷失败",
|
||||
zap.String("task_type", taskType),
|
||||
zap.Uint("task_id", taskID),
|
||||
zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
queueName := constants.QueueForTaskType(taskType)
|
||||
taskMessage := asynq.NewTask(
|
||||
taskType,
|
||||
payloadBytes,
|
||||
asynq.Queue(queueName),
|
||||
asynq.TaskID(fmt.Sprintf("import-rescue:%s:%d", taskType, taskID)),
|
||||
asynq.Unique(30*time.Minute),
|
||||
)
|
||||
if _, err := asynqClient.EnqueueContext(ctx, taskMessage); err != nil {
|
||||
appLogger.Warn("提交导入补偿任务失败",
|
||||
zap.String("task_type", taskType),
|
||||
zap.String("queue", queueName),
|
||||
zap.Uint("task_id", taskID),
|
||||
zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
appLogger.Info("导入补偿任务已提交",
|
||||
zap.String("task_type", taskType),
|
||||
zap.String("queue", queueName),
|
||||
zap.Uint("task_id", taskID))
|
||||
}
|
||||
|
||||
// shutdownWorker 按当前实例实际启动过的模块执行优雅关闭。
|
||||
func shutdownWorker(
|
||||
cancel context.CancelFunc,
|
||||
|
||||
Reference in New Issue
Block a user