实现七月迭代公共技术基础
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/bootstrap"
|
||||
"github.com/break/junhong_cmp_fiber/internal/gateway"
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/messaging/outbox"
|
||||
"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"
|
||||
@@ -36,6 +37,7 @@ const (
|
||||
workerModulePollingInitializer = "polling_initializer"
|
||||
workerModulePollingScheduler = "polling_scheduler"
|
||||
workerModuleAsynqScheduler = "asynq_scheduler"
|
||||
workerModuleOutboxRelay = "outbox_relay"
|
||||
importRescueLimit = 500 // 启动补偿单次扫描的最大导入任务数
|
||||
)
|
||||
|
||||
@@ -55,6 +57,8 @@ type workerRuntime struct {
|
||||
asynqClient *asynq.Client
|
||||
workerResult *bootstrap.WorkerBootstrapResult
|
||||
workerServer *queue.Server
|
||||
outboxQueueClient *queue.Client
|
||||
outboxConsumers *outbox.ConsumerRegistry
|
||||
pollingConfigMgr *polling.PollingConfigManager
|
||||
pollingQueueMgr *polling.PollingQueueManager
|
||||
pollingIotCardStore *postgres.IotCardStore
|
||||
@@ -124,6 +128,9 @@ func runWorker(cfg *config.Config) {
|
||||
|
||||
taskHandler := createTaskHandler(runtime, appLogger)
|
||||
taskHandler.RegisterHandlers()
|
||||
outboxHandler := outbox.NewHandler(runtime.outboxConsumers)
|
||||
taskHandler.GetMux().HandleFunc(constants.TaskTypeOutboxDeliver, outboxHandler.Handle)
|
||||
startOutboxRelay(ctx, runtime, cfg.Worker.InstanceName, appLogger)
|
||||
rescuePendingImportTasks(ctx, runtime, appLogger)
|
||||
|
||||
appLogger.Info("Worker 服务器配置完成",
|
||||
@@ -149,7 +156,7 @@ func runWorker(cfg *config.Config) {
|
||||
// buildWorkerModuleStatus 根据角色生成启用/禁用模块列表,供启动日志和人工验收使用。
|
||||
func buildWorkerModuleStatus(role string) workerModuleStatus {
|
||||
status := workerModuleStatus{
|
||||
enabled: []string{workerModuleQueueServer},
|
||||
enabled: []string{workerModuleQueueServer, workerModuleOutboxRelay},
|
||||
}
|
||||
|
||||
if runsSingletonModules(role) {
|
||||
@@ -233,6 +240,7 @@ func initWorkerRuntime(ctx context.Context, cfg *config.Config, appLogger *zap.L
|
||||
}
|
||||
|
||||
workerServer := queue.NewServer(redisClient, &cfg.Queue, appLogger)
|
||||
outboxQueueClient := queue.NewClient(redisClient, appLogger)
|
||||
|
||||
pollingConfigStore := postgres.NewPollingConfigStore(db)
|
||||
pollingConfigMgr := polling.NewPollingConfigManager(pollingConfigStore, redisClient, appLogger)
|
||||
@@ -275,6 +283,8 @@ func initWorkerRuntime(ctx context.Context, cfg *config.Config, appLogger *zap.L
|
||||
asynqClient: asynqClient,
|
||||
workerResult: workerResult,
|
||||
workerServer: workerServer,
|
||||
outboxQueueClient: outboxQueueClient,
|
||||
outboxConsumers: outbox.NewConsumerRegistry(),
|
||||
pollingConfigMgr: pollingConfigMgr,
|
||||
pollingQueueMgr: pollingQueueMgr,
|
||||
pollingIotCardStore: pollingIotCardStore,
|
||||
@@ -285,6 +295,11 @@ func initWorkerRuntime(ctx context.Context, cfg *config.Config, appLogger *zap.L
|
||||
|
||||
// close 在 Worker 退出时关闭共享客户端与数据库连接。
|
||||
func (r *workerRuntime) close(appLogger *zap.Logger) {
|
||||
if r.outboxQueueClient != nil {
|
||||
if err := r.outboxQueueClient.Close(); err != nil {
|
||||
appLogger.Error("关闭 Outbox 队列客户端失败", zap.Error(err))
|
||||
}
|
||||
}
|
||||
if r.asynqClient != nil {
|
||||
if err := r.asynqClient.Close(); err != nil {
|
||||
appLogger.Error("关闭 Asynq 客户端失败", zap.Error(err))
|
||||
@@ -305,6 +320,39 @@ func (r *workerRuntime) close(appLogger *zap.Logger) {
|
||||
}
|
||||
}
|
||||
|
||||
// startOutboxRelay 启动公共 Outbox 的可取消轮询;多个 Worker 依靠 PostgreSQL 租约并发协调。
|
||||
func startOutboxRelay(ctx context.Context, runtime *workerRuntime, instanceName string, appLogger *zap.Logger) {
|
||||
owner := instanceName
|
||||
if owner == "" {
|
||||
owner = fmt.Sprintf("worker-%d", os.Getpid())
|
||||
}
|
||||
relay, err := outbox.NewRelay(
|
||||
runtime.db,
|
||||
outbox.NewQueuePublisher(runtime.outboxQueueClient),
|
||||
appLogger,
|
||||
outbox.RelayOptions{Owner: owner},
|
||||
)
|
||||
if err != nil {
|
||||
appLogger.Fatal("初始化 Outbox Relay 失败", zap.Error(err))
|
||||
}
|
||||
go func() {
|
||||
ticker := time.NewTicker(constants.OutboxRelayPollInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
if _, err := relay.ProcessBatch(ctx); err != nil && ctx.Err() == nil {
|
||||
appLogger.Error("Outbox Relay 批次处理失败",
|
||||
zap.String("component", "outbox_relay"), zap.String("error_code", "OUTBOX_RELAY_BATCH_FAILED"), zap.Error(err))
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}()
|
||||
appLogger.Info("Outbox Relay 已启动", zap.String("lease_owner", owner))
|
||||
}
|
||||
|
||||
// startPollingInitializer 为 leader/all 启动渐进式初始化器,并保留配置变更重启逻辑。
|
||||
func startPollingInitializer(ctx context.Context, runtime *workerRuntime, appLogger *zap.Logger) *polling.PollingInitializer {
|
||||
pollingInitializer := polling.NewPollingInitializer(
|
||||
|
||||
Reference in New Issue
Block a user