chore: 初始开源发布
iotlcm-worker-svr:异步执行中枢(RabbitMQ 消费者 + 全量同步编排)。 - Go module:iotlcm-worker-svr → git.5iot.com/iotlcm-public/iotlcm-worker-svr(214 文件) - 凭据与内部标识脱敏:MySQL/Redis 凭据、私网与生产 IP、内网域名、 库名 xiujie_admin → iotlcm、哨兵名占位 - 159 处真实手机号 → 合法 11 位编造号(保留号段与后 4 位,维持互异, 不破坏「校验 MSISDN 格式」等测试断言) - 移除内部资产:CI 触发器、Jenkins 触发测试文档、预演门禁与镜像 digest 记录 - Dockerfile 改用公共基础镜像(golang:1.25-alpine → alpine:3.23), 移除写死的 git 凭据 - README 重写(架构/全量同步模型/配置/已知失败),补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 已知:3 个测试包在源仓同一提交上同样失败(client/upstream、 service/allocate、service/inbound),属既有缺陷,本仓未引入亦未修复。
This commit is contained in:
@@ -0,0 +1,752 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gredis"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gmode"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/batch"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/client/upstream"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consts"
|
||||
rabbitmqconsumer "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consumer/rabbitmq"
|
||||
zsetconsumer "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consumer/zset"
|
||||
fullsyncarchive "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/fullsync/archive"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/fullsync/httpapi"
|
||||
fullsyncruntime "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/fullsync/runtime"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/fullsync/supply"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/library/tdengine"
|
||||
dailyusageproducer "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/producer/dailyusage"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/producer/imei"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/producer/monthlyusage"
|
||||
monthlyhistoryproducer "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/producer/monthlyusagehistory"
|
||||
simstatusproducer "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/producer/simstatus"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/queue/zset"
|
||||
batchrepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/batch"
|
||||
cancelrepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/cancel"
|
||||
datarepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/data"
|
||||
imeirepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/imei"
|
||||
inboundrepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/inbound"
|
||||
lifecyclerepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/lifecycle"
|
||||
monthlyusagerepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/monthlyusage"
|
||||
simstatusrepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/simstatus"
|
||||
trafficusagerepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/trafficusage"
|
||||
unsubrepository "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/repository/unsub"
|
||||
addpackageservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/addpackage"
|
||||
allocateservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/allocate"
|
||||
balanceservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/balance"
|
||||
cancelservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/cancel"
|
||||
dailyusageservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/dailyusage"
|
||||
dataservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/data"
|
||||
imeiservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/imei"
|
||||
inboundservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/inbound"
|
||||
lifecycleservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/lifecycle"
|
||||
monthlyusageservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/monthlyusage"
|
||||
monthlyhistoryservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/monthlyusagehistory"
|
||||
packagecustomerservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/packagecustomer"
|
||||
renewchangeservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/renewchange"
|
||||
simstatusservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/simstatus"
|
||||
trafficcutoffconfigservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/trafficcutoffconfig"
|
||||
trafficusageservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/trafficusage"
|
||||
unsubservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/unsub"
|
||||
warningconfigservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/warningconfig"
|
||||
workorderservice "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/service/workorder"
|
||||
)
|
||||
|
||||
const (
|
||||
shutdownTimeout = 10 * time.Second
|
||||
consumerRestartInitialDelay = 2 * time.Second
|
||||
consumerRestartMaxDelay = 30 * time.Second
|
||||
consumerRestartBackoffResetAfter = time.Minute
|
||||
)
|
||||
|
||||
const monthlyUsageTemplateQPSConfigKey = "trafficUsageFullSync.consumer.templateQPS"
|
||||
|
||||
func loadMonthlyUsageTemplateQPS(ctx context.Context) map[string]float64 {
|
||||
value, err := g.Cfg().Get(ctx, monthlyUsageTemplateQPSConfigKey)
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "load monthly usage template QPS config failed: %+v", err)
|
||||
return nil
|
||||
}
|
||||
if value == nil || value.IsNil() {
|
||||
return nil
|
||||
}
|
||||
var configured map[string]float64
|
||||
if err = value.Scan(&configured); err != nil {
|
||||
g.Log().Warningf(ctx, "parse monthly usage template QPS config failed: %+v", err)
|
||||
return nil
|
||||
}
|
||||
return configured
|
||||
}
|
||||
|
||||
func loadSimStatusTemplateQPS(ctx context.Context) map[string]float64 {
|
||||
value, err := g.Cfg().Get(ctx, "simStatusQueueSync.consumer.templateQPS")
|
||||
if err != nil || value == nil || value.IsNil() {
|
||||
return nil
|
||||
}
|
||||
var configured map[string]float64
|
||||
if err = value.Scan(&configured); err != nil {
|
||||
g.Log().Warningf(ctx, "parse sim status template QPS config failed: %+v", err)
|
||||
return nil
|
||||
}
|
||||
return configured
|
||||
}
|
||||
|
||||
func loadImeiTemplateQPS(ctx context.Context) map[string]float64 {
|
||||
value, err := g.Cfg().Get(ctx, "imeiFullSync.consumer.templateQPS")
|
||||
if err != nil || value == nil || value.IsNil() {
|
||||
return nil
|
||||
}
|
||||
var configured map[string]float64
|
||||
if err = value.Scan(&configured); err != nil {
|
||||
g.Log().Warningf(ctx, "parse imei template QPS config failed: %+v", err)
|
||||
return nil
|
||||
}
|
||||
return configured
|
||||
}
|
||||
|
||||
func loadSimStatusRunPrefix(ctx context.Context) (string, error) {
|
||||
prefix := strings.TrimSpace(g.Cfg().MustGet(ctx, "simStatusQueueSync.redisKeyPrefix", "sim-status:").String())
|
||||
if prefix == "" {
|
||||
return "", gerror.New("sim status redisKeyPrefix must be nonempty")
|
||||
}
|
||||
return prefix, nil
|
||||
}
|
||||
|
||||
func loadDailyUsageTemplateQPS(ctx context.Context) map[string]float64 {
|
||||
value, err := g.Cfg().Get(ctx, "trafficUsageDailySync.consumer.templateQPS")
|
||||
if err != nil || value == nil || value.IsNil() {
|
||||
return nil
|
||||
}
|
||||
var configured map[string]float64
|
||||
if err = value.Scan(&configured); err != nil {
|
||||
g.Log().Warningf(ctx, "parse daily usage template QPS config failed: %+v", err)
|
||||
return nil
|
||||
}
|
||||
return configured
|
||||
}
|
||||
|
||||
func loadDailyUsageRunPrefix(ctx context.Context) (string, error) {
|
||||
prefix := strings.TrimSpace(g.Cfg().MustGet(ctx, "trafficUsageDailySync.redisKeyPrefix", "daily:").String())
|
||||
if prefix == "" {
|
||||
return "", gerror.New("daily usage redisKeyPrefix must be nonempty")
|
||||
}
|
||||
return prefix, nil
|
||||
}
|
||||
|
||||
func loadImeiRunPrefix(ctx context.Context) (string, error) {
|
||||
prefix := strings.TrimSpace(g.Cfg().MustGet(ctx, "imeiFullSync.redisKeyPrefix", "imei:").String())
|
||||
if prefix == "" {
|
||||
return "", gerror.New("imei redisKeyPrefix must be nonempty")
|
||||
}
|
||||
return prefix, nil
|
||||
}
|
||||
|
||||
func loadMonthlyHistoryRunPrefix(ctx context.Context) (string, error) {
|
||||
prefix := strings.TrimSpace(g.Cfg().MustGet(ctx, "trafficUsageMonthlyHistorySync.redisKeyPrefix", "monthly-history:").String())
|
||||
if prefix == "" {
|
||||
return "", gerror.New("monthly usage history redisKeyPrefix must be nonempty")
|
||||
}
|
||||
return prefix, nil
|
||||
}
|
||||
|
||||
func loadMonthlyHistoryTemplateQPS(ctx context.Context) map[string]float64 {
|
||||
value, err := g.Cfg().Get(ctx, "trafficUsageMonthlyHistorySync.consumer.templateQPS")
|
||||
if err != nil || value == nil || value.IsNil() {
|
||||
return nil
|
||||
}
|
||||
var configured map[string]float64
|
||||
if err = value.Scan(&configured); err != nil {
|
||||
g.Log().Warningf(ctx, "parse monthly usage history template QPS config failed: %+v", err)
|
||||
return nil
|
||||
}
|
||||
return configured
|
||||
}
|
||||
|
||||
func superviseZSetConsumer(ctx context.Context, eventPrefix string, run func(context.Context) error) {
|
||||
retryDelay := consumerRestartInitialDelay
|
||||
for {
|
||||
startedAt := time.Now()
|
||||
runErr := run(ctx)
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
if time.Since(startedAt) >= consumerRestartBackoffResetAfter {
|
||||
retryDelay = consumerRestartInitialDelay
|
||||
}
|
||||
|
||||
errorText := ""
|
||||
if runErr != nil {
|
||||
errorText = runErr.Error()
|
||||
}
|
||||
g.Log().Warningf(ctx, "event=%s_consumer status=restart_scheduled retryDelayMs=%d error=%q", eventPrefix, retryDelay.Milliseconds(), errorText)
|
||||
|
||||
timer := time.NewTimer(retryDelay)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
retryDelay *= 2
|
||||
if retryDelay > consumerRestartMaxDelay {
|
||||
retryDelay = consumerRestartMaxDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadFullSyncArchivePrefixes(ctx context.Context) ([]string, error) {
|
||||
trafficPrefix := strings.TrimSpace(g.Cfg().MustGet(ctx, "trafficUsageFullSync.redisKeyPrefix", "flow:").String())
|
||||
if trafficPrefix == "" {
|
||||
trafficPrefix = "flow:"
|
||||
}
|
||||
statusPrefix, err := loadSimStatusRunPrefix(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dailyPrefix, err := loadDailyUsageRunPrefix(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
monthlyHistoryPrefix, err := loadMonthlyHistoryRunPrefix(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imeiPrefix, err := loadImeiRunPrefix(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Duplicate prefixes would make Catalog and SimpleReplay read the same run
|
||||
// hash twice. Deduplicate while preserving the traffic-first order.
|
||||
prefixes := make([]string, 0, 5)
|
||||
seen := make(map[string]struct{}, 5)
|
||||
for _, prefix := range []string{trafficPrefix, statusPrefix, dailyPrefix, monthlyHistoryPrefix, imeiPrefix} {
|
||||
if _, exists := seen[prefix]; exists {
|
||||
continue
|
||||
}
|
||||
seen[prefix] = struct{}{}
|
||||
prefixes = append(prefixes, prefix)
|
||||
}
|
||||
return prefixes, nil
|
||||
}
|
||||
|
||||
func Run(ctx context.Context) error {
|
||||
workerCtx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
if err := initialize(workerCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
upstreamClient = upstream.New()
|
||||
usageRepository = trafficusagerepository.New()
|
||||
trafficUsage = trafficusageservice.New(upstreamClient, usageRepository)
|
||||
workOrder = workorderservice.New()
|
||||
balance = balanceservice.New()
|
||||
inboundRepo = inboundrepository.New()
|
||||
inboundExecutor = inboundservice.NewExecutorWithUpstream(inboundRepo, upstreamClient)
|
||||
lifecycleRepo = lifecyclerepository.New()
|
||||
lifecycleExecutor = lifecycleservice.NewExecutor(lifecycleRepo, upstreamClient)
|
||||
cancelRepo = cancelrepository.New()
|
||||
cancelExecutor = cancelservice.NewExecutor(cancelRepo)
|
||||
renewChangeRepo = renewchangeservice.NewRepository()
|
||||
addPackageCardExecutor = addpackageservice.NewCardExecutor(renewChangeRepo)
|
||||
addPackagePoolExecutor = addpackageservice.NewPoolExecutor(renewChangeRepo)
|
||||
renewPrepareExecutor = renewchangeservice.NewPrepareExecutor(renewChangeRepo)
|
||||
prepareExecutor = addpackageservice.NewPrepareMux(renewChangeRepo, renewPrepareExecutor)
|
||||
renewExecutor = renewchangeservice.NewRenewExecutor(renewChangeRepo)
|
||||
changeExecutor = renewchangeservice.NewChangeExecutor(renewChangeRepo)
|
||||
dataRepo = datarepository.New()
|
||||
dataExecutor = dataservice.NewExecutor(dataRepo, upstreamClient)
|
||||
unsubRepo = unsubrepository.New()
|
||||
unsubExecutor = unsubservice.NewExecutor(unsubRepo)
|
||||
restartExecutor = lifecycleservice.NewRestartExecutor(lifecycleRepo, upstreamClient)
|
||||
batchRepository = batchrepository.New()
|
||||
batchDispatcher = batch.NewDispatcher(lifecycleExecutor)
|
||||
batchRuntime = batch.NewRuntime(
|
||||
batchRepository,
|
||||
batchDispatcher,
|
||||
batch.WithExecutionTimeout(g.Cfg().MustGet(workerCtx, "rabbitmq.batchExecute.executionTimeout", "0s").Duration()),
|
||||
batch.WithFinalizationTimeout(g.Cfg().MustGet(workerCtx, "rabbitmq.batchExecute.finalizationTimeout", "10s").Duration()),
|
||||
)
|
||||
allocateExecutor = allocateservice.NewExecutor()
|
||||
warningConfigExecutor = warningconfigservice.NewExecutor()
|
||||
trafficCutoffConfigExecutor = trafficcutoffconfigservice.NewExecutor()
|
||||
packageCustomerSync = packagecustomerservice.New()
|
||||
)
|
||||
var (
|
||||
monthlyProducer *monthlyusage.Producer
|
||||
monthlyConsumer *zsetconsumer.Consumer
|
||||
statusProducer *simstatusproducer.Producer
|
||||
statusConsumer *zsetconsumer.Consumer
|
||||
dailyProducer *dailyusageproducer.Producer
|
||||
dailyConsumer *zsetconsumer.Consumer
|
||||
monthlyHistoryProducer *monthlyhistoryproducer.Producer
|
||||
monthlyHistoryConsumer *zsetconsumer.Consumer
|
||||
imeiProducer *imei.Producer
|
||||
imeiConsumer *zsetconsumer.Consumer
|
||||
)
|
||||
if g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.enabled", false).Bool() {
|
||||
prefix := g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.redisKeyPrefix", "flow:").String()
|
||||
store, err := zset.New(g.Redis(), prefix)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize monthly usage Redis store failed")
|
||||
}
|
||||
store.EnableArchiveIndex(g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.enabled", false).Bool())
|
||||
producerConfig, configErr := loadMonthlyUsageProducerConfig(workerCtx)
|
||||
if configErr != nil {
|
||||
return configErr
|
||||
}
|
||||
var supplyTracker *supply.Tracker
|
||||
if g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.supplyObservation.enabled", false).Bool() {
|
||||
supplyTracker = supply.NewTracker(g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.enabled", false).Bool())
|
||||
producerConfig.SupplyObservation = supplyTracker
|
||||
}
|
||||
monthlyProducer, err = monthlyusage.New(&monthlyusage.DatabaseScanner{}, store, producerConfig)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize monthly usage producer failed")
|
||||
}
|
||||
monthlyUsageClient := upstream.NewWithCurrentUsageClient(
|
||||
g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.chanClientName", "").String(),
|
||||
g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.chanClientTimeout", "90s").Duration(),
|
||||
)
|
||||
usageHandler, handlerErr := monthlyusageservice.NewHandler(monthlyUsageClient, monthlyusagerepository.NewRepository())
|
||||
if handlerErr != nil {
|
||||
return gerror.Wrap(handlerErr, "initialize monthly usage handler failed")
|
||||
}
|
||||
monthlyConsumer, err = zsetconsumer.New(store, usageHandler, zsetconsumer.Config{
|
||||
SupplyObservation: supplyTracker,
|
||||
LockKey: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.lockKey", "flow:consumer:active").String(),
|
||||
LockTTL: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.lockTTL", "1m").Duration(),
|
||||
PollInterval: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.pollInterval", "1s").Duration(),
|
||||
LeaseTTL: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.leaseTTL", "3m").Duration(),
|
||||
TaskTimeout: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.taskTimeout", "2m").Duration(),
|
||||
ClaimBatch: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.claimBatch", 100).Int(),
|
||||
MobileConcurrency: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.mobileConcurrency", 30).Int(),
|
||||
TelecomConcurrency: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.telecomConcurrency", 20).Int(),
|
||||
GenericConcurrency: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.genericConcurrency", 1).Int(),
|
||||
GenericQPS: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.genericQPS", 1.5).Float64(),
|
||||
TemplateQPS: loadMonthlyUsageTemplateQPS(workerCtx),
|
||||
RoundRetryEnabled: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.retry.enabled", true).Bool(),
|
||||
MaxRounds: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.retry.maxRounds", 5).Int(),
|
||||
FailureRetention: g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.retry.failureRetention", "720h").Duration(),
|
||||
ShouldRetryNextRound: func(result zsetconsumer.Result) bool {
|
||||
return !result.Success && result.Retryable
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize monthly usage consumer failed")
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "trafficUsageFullSync.consumer.enabled", false).Bool() {
|
||||
go superviseZSetConsumer(workerCtx, "monthly_usage", monthlyConsumer.Run)
|
||||
}
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.enabled", false).Bool() {
|
||||
prefix, prefixErr := loadDailyUsageRunPrefix(workerCtx)
|
||||
if prefixErr != nil {
|
||||
return prefixErr
|
||||
}
|
||||
store, err := zset.New(g.Redis(), prefix)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize daily usage Redis store failed")
|
||||
}
|
||||
store.EnableArchiveIndex(g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.enabled", false).Bool())
|
||||
dailyScanMode, dailyScanConcurrency, scanErr := loadHistoryUsageScanSettings(workerCtx, "trafficUsageDailySync")
|
||||
if scanErr != nil {
|
||||
return scanErr
|
||||
}
|
||||
dailyProducer, err = dailyusageproducer.New(&dailyusageproducer.DatabaseScanner{}, store, dailyusageproducer.Config{
|
||||
ScanMode: dailyScanMode,
|
||||
ScanConcurrency: dailyScanConcurrency,
|
||||
PageSize: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.producer.pageSize", 100).Int(),
|
||||
GlobalHighWaterMark: int64(g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.producer.globalHighWaterMark", 0).Int64()),
|
||||
ChannelHighWaterMark: int64(g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.producer.channelHighWaterMark", 0).Int64()),
|
||||
PollInterval: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.producer.pollInterval", "2s").Duration(),
|
||||
RunLockKey: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.producer.runLockKey", "daily:producer:active").String(),
|
||||
RunLockTTL: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.producer.runLockTTL", "5m").Duration(),
|
||||
RoundRetryEnabled: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.retry.enabled", true).Bool(),
|
||||
MaxRounds: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.retry.maxRounds", 3).Int(),
|
||||
FailureRetention: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.retry.failureRetention", "720h").Duration(),
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize daily usage producer failed")
|
||||
}
|
||||
dailyHandler, handlerErr := dailyusageservice.NewHandler(upstream.New(), trafficusagerepository.New())
|
||||
if handlerErr != nil {
|
||||
return gerror.Wrap(handlerErr, "initialize daily usage handler failed")
|
||||
}
|
||||
dailyConsumer, err = zsetconsumer.New(store, dailyHandler, zsetconsumer.Config{
|
||||
RunQueueEnabled: true,
|
||||
RoundRetryEnabled: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.retry.enabled", true).Bool(),
|
||||
MaxRounds: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.retry.maxRounds", 3).Int(),
|
||||
FailureRetention: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.retry.failureRetention", "720h").Duration(),
|
||||
ShouldRetryNextRound: dailyusageservice.ShouldRetryNextRound,
|
||||
BlockedTasksAsFailures: true,
|
||||
EventPrefix: "daily_usage",
|
||||
LockKey: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.lockKey", "daily:consumer:active").String(),
|
||||
LockTTL: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.lockTTL", "1m").Duration(),
|
||||
PollInterval: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.pollInterval", "1s").Duration(),
|
||||
LeaseTTL: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.leaseTTL", "3m").Duration(),
|
||||
TaskTimeout: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.taskTimeout", "2m").Duration(),
|
||||
ClaimBatch: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.claimBatch", 100).Int(),
|
||||
MobileConcurrency: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.mobileConcurrency", 30).Int(),
|
||||
TelecomConcurrency: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.telecomConcurrency", 8).Int(),
|
||||
GenericConcurrency: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.genericConcurrency", 1).Int(),
|
||||
GenericQPS: g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.genericQPS", 1.5).Float64(),
|
||||
TemplateQPS: loadDailyUsageTemplateQPS(workerCtx),
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize daily usage consumer failed")
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "trafficUsageDailySync.consumer.enabled", false).Bool() {
|
||||
go superviseZSetConsumer(workerCtx, "daily_usage", dailyConsumer.Run)
|
||||
}
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.enabled", false).Bool() {
|
||||
prefix, prefixErr := loadMonthlyHistoryRunPrefix(workerCtx)
|
||||
if prefixErr != nil {
|
||||
return prefixErr
|
||||
}
|
||||
store, err := zset.New(g.Redis(), prefix)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize monthly usage history Redis store failed")
|
||||
}
|
||||
store.EnableArchiveIndex(g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.enabled", false).Bool())
|
||||
monthlyHistoryScanMode, monthlyHistoryScanConcurrency, scanErr := loadHistoryUsageScanSettings(workerCtx, "trafficUsageMonthlyHistorySync")
|
||||
if scanErr != nil {
|
||||
return scanErr
|
||||
}
|
||||
monthlyHistoryProducer, err = monthlyhistoryproducer.New(&monthlyhistoryproducer.DatabaseScanner{}, store, monthlyhistoryproducer.Config{
|
||||
ScanMode: monthlyHistoryScanMode,
|
||||
ScanConcurrency: monthlyHistoryScanConcurrency,
|
||||
PageSize: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.producer.pageSize", 100).Int(),
|
||||
GlobalHighWaterMark: int64(g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.producer.globalHighWaterMark", 0).Int64()),
|
||||
ChannelHighWaterMark: int64(g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.producer.channelHighWaterMark", 0).Int64()),
|
||||
PollInterval: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.producer.pollInterval", "2s").Duration(),
|
||||
RunLockKey: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.producer.runLockKey", "monthly-history:producer:active").String(),
|
||||
RunLockTTL: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.producer.runLockTTL", "5m").Duration(),
|
||||
RoundRetryEnabled: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.retry.enabled", true).Bool(),
|
||||
MaxRounds: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.retry.maxRounds", 3).Int(),
|
||||
FailureRetention: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.retry.failureRetention", "720h").Duration(),
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize monthly usage history producer failed")
|
||||
}
|
||||
monthlyHistoryHandler, handlerErr := monthlyhistoryservice.NewHandler(upstream.New(), trafficusagerepository.New())
|
||||
if handlerErr != nil {
|
||||
return gerror.Wrap(handlerErr, "initialize monthly usage history handler failed")
|
||||
}
|
||||
monthlyHistoryConsumer, err = zsetconsumer.New(store, monthlyHistoryHandler, zsetconsumer.Config{
|
||||
RunQueueEnabled: true,
|
||||
RoundRetryEnabled: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.retry.enabled", true).Bool(),
|
||||
MaxRounds: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.retry.maxRounds", 3).Int(),
|
||||
FailureRetention: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.retry.failureRetention", "720h").Duration(),
|
||||
ShouldRetryNextRound: monthlyhistoryservice.ShouldRetryNextRound,
|
||||
BlockedTasksAsFailures: true,
|
||||
EventPrefix: "monthly_usage_history",
|
||||
LockKey: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.lockKey", "monthly-history:consumer:active").String(),
|
||||
LockTTL: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.lockTTL", "1m").Duration(),
|
||||
PollInterval: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.pollInterval", "1s").Duration(),
|
||||
LeaseTTL: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.leaseTTL", "3m").Duration(),
|
||||
TaskTimeout: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.taskTimeout", "2m").Duration(),
|
||||
ClaimBatch: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.claimBatch", 100).Int(),
|
||||
MobileConcurrency: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.mobileConcurrency", 30).Int(),
|
||||
TelecomConcurrency: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.telecomConcurrency", 8).Int(),
|
||||
GenericConcurrency: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.genericConcurrency", 1).Int(),
|
||||
GenericQPS: g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.genericQPS", 1.5).Float64(),
|
||||
TemplateQPS: loadMonthlyHistoryTemplateQPS(workerCtx),
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize monthly usage history consumer failed")
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "trafficUsageMonthlyHistorySync.consumer.enabled", false).Bool() {
|
||||
go superviseZSetConsumer(workerCtx, "monthly_usage_history", monthlyHistoryConsumer.Run)
|
||||
}
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "simStatusQueueSync.enabled", false).Bool() {
|
||||
prefix, prefixErr := loadSimStatusRunPrefix(workerCtx)
|
||||
if prefixErr != nil {
|
||||
return prefixErr
|
||||
}
|
||||
store, err := zset.New(g.Redis(), prefix)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize sim status Redis store failed")
|
||||
}
|
||||
store.EnableArchiveIndex(g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.enabled", false).Bool())
|
||||
statusProducer, err = simstatusproducer.New(&simstatusproducer.DatabaseScanner{}, store, simstatusproducer.Config{
|
||||
PageSize: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.producer.pageSize", 1000).Int(),
|
||||
GlobalHighWaterMark: int64(g.Cfg().MustGet(workerCtx, "simStatusQueueSync.producer.globalHighWaterMark", 0).Int64()),
|
||||
PollInterval: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.producer.pollInterval", "1s").Duration(),
|
||||
RunLockKey: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.producer.runLockKey", "sim-status:producer:active").String(),
|
||||
RunLockTTL: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.producer.runLockTTL", "5m").Duration(),
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize sim status producer failed")
|
||||
}
|
||||
statusHandler, handlerErr := simstatusservice.NewHandler(
|
||||
upstream.New(),
|
||||
simstatusrepository.NewRepository(),
|
||||
g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.networkRetries", 2).Int(),
|
||||
)
|
||||
if handlerErr != nil {
|
||||
return gerror.Wrap(handlerErr, "initialize sim status handler failed")
|
||||
}
|
||||
statusConsumer, err = zsetconsumer.New(store, statusHandler, zsetconsumer.Config{
|
||||
RunQueueEnabled: true,
|
||||
RoundRetryEnabled: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.retry.enabled", true).Bool(),
|
||||
MaxRounds: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.retry.maxRounds", 3).Int(),
|
||||
FailureRetention: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.retry.failureRetention", "720h").Duration(),
|
||||
ShouldRetryNextRound: simstatusservice.ShouldRetryNextRound,
|
||||
BlockedTasksAsFailures: true,
|
||||
EventPrefix: "sim_status_queue",
|
||||
LockKey: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.lockKey", "sim-status:consumer:active").String(),
|
||||
LockTTL: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.lockTTL", "1m").Duration(),
|
||||
PollInterval: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.pollInterval", "1s").Duration(),
|
||||
LeaseTTL: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.leaseTTL", "3m").Duration(),
|
||||
TaskTimeout: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.taskTimeout", "2m").Duration(),
|
||||
ClaimBatch: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.claimBatch", 100).Int(),
|
||||
MobileConcurrency: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.mobileConcurrency", 100).Int(),
|
||||
TelecomConcurrency: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.telecomConcurrency", 8).Int(),
|
||||
GenericConcurrency: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.genericConcurrency", 1).Int(),
|
||||
GenericQPS: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.genericQPS", 1.5).Float64(),
|
||||
TemplateQPS: loadSimStatusTemplateQPS(workerCtx),
|
||||
FailurePoolAlertThreshold: g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.failurePoolAlertThreshold", 50000).Int64(),
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize sim status consumer failed")
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "simStatusQueueSync.consumer.enabled", false).Bool() {
|
||||
go superviseZSetConsumer(workerCtx, "sim_status_queue", statusConsumer.Run)
|
||||
}
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "imeiFullSync.enabled", false).Bool() {
|
||||
prefix, prefixErr := loadImeiRunPrefix(workerCtx)
|
||||
if prefixErr != nil {
|
||||
return prefixErr
|
||||
}
|
||||
store, err := zset.New(g.Redis(), prefix)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize imei Redis store failed")
|
||||
}
|
||||
store.EnableArchiveIndex(g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.enabled", false).Bool())
|
||||
// g.Redis may be initialized from g.Cfg without registering GetConfig.
|
||||
leaseConfig, configured := gredis.GetConfig()
|
||||
if !configured {
|
||||
value, configErr := g.Cfg().Get(workerCtx, "redis.default")
|
||||
if configErr != nil {
|
||||
return gerror.Wrap(configErr, "load IMEI lease Redis configuration failed")
|
||||
}
|
||||
leaseConfig, err = gredis.ConfigFromMap(value.Map())
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "parse IMEI lease Redis configuration failed")
|
||||
}
|
||||
}
|
||||
imeiProducer, err = imei.New(&imei.DatabaseScanner{}, store, imei.Config{
|
||||
PageSize: g.Cfg().MustGet(workerCtx, "imeiFullSync.producer.pageSize", 1000).Int(),
|
||||
GlobalHighWaterMark: int64(g.Cfg().MustGet(workerCtx, "imeiFullSync.producer.globalHighWaterMark", 0).Int64()),
|
||||
PollInterval: g.Cfg().MustGet(workerCtx, "imeiFullSync.producer.pollInterval", "1s").Duration(),
|
||||
RunLockKey: g.Cfg().MustGet(workerCtx, "imeiFullSync.producer.runLockKey", "imei:producer:active").String(),
|
||||
RunLockTTL: g.Cfg().MustGet(workerCtx, "imeiFullSync.producer.runLockTTL", "5m").Duration(),
|
||||
LeaseRedisConfig: leaseConfig,
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize imei producer failed")
|
||||
}
|
||||
imeiHandler, handlerErr := imeiservice.NewHandler(
|
||||
upstream.NewWithImeiClient(
|
||||
g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.chanClientName", "").String(),
|
||||
g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.chanClientTimeout", "30s").Duration(),
|
||||
),
|
||||
imeirepository.NewRepository(),
|
||||
g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.networkRetries", 2).Int(),
|
||||
)
|
||||
if handlerErr != nil {
|
||||
return gerror.Wrap(handlerErr, "initialize imei handler failed")
|
||||
}
|
||||
imeiConsumer, err = zsetconsumer.New(store, imeiHandler, zsetconsumer.Config{
|
||||
RunQueueEnabled: true,
|
||||
RoundRetryEnabled: g.Cfg().MustGet(workerCtx, "imeiFullSync.retry.enabled", true).Bool(),
|
||||
MaxRounds: g.Cfg().MustGet(workerCtx, "imeiFullSync.retry.maxRounds", 5).Int(),
|
||||
FailureRetention: g.Cfg().MustGet(workerCtx, "imeiFullSync.retry.failureRetention", "720h").Duration(),
|
||||
ShouldRetryNextRound: imeiservice.ShouldRetryNextRound,
|
||||
BlockedTasksAsFailures: true,
|
||||
EventPrefix: "imei_queue",
|
||||
LockKey: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.lockKey", "imei:consumer:active").String(),
|
||||
LockTTL: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.lockTTL", "1m").Duration(),
|
||||
PollInterval: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.pollInterval", "1s").Duration(),
|
||||
LeaseTTL: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.leaseTTL", "3m").Duration(),
|
||||
TaskTimeout: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.taskTimeout", "2m").Duration(),
|
||||
ClaimBatch: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.claimBatch", 100).Int(),
|
||||
MobileConcurrency: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.mobileConcurrency", 100).Int(),
|
||||
TelecomConcurrency: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.telecomConcurrency", 8).Int(),
|
||||
GenericConcurrency: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.genericConcurrency", 1).Int(),
|
||||
GenericQPS: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.genericQPS", 1.5).Float64(),
|
||||
TemplateQPS: loadImeiTemplateQPS(workerCtx),
|
||||
// 失败池是唯一随卡数线性增长的每卡结构(单条约 1KB),
|
||||
// 且会保留整个 failureRetention。超阀值只告警不截断。
|
||||
FailurePoolAlertThreshold: g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.failurePoolAlertThreshold", 50000).Int64(),
|
||||
})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize imei consumer failed")
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "imeiFullSync.consumer.enabled", false).Bool() {
|
||||
go superviseZSetConsumer(workerCtx, "imei_queue", imeiConsumer.Run)
|
||||
}
|
||||
}
|
||||
var rabbitMQConsumers *rabbitmqconsumer.Consumers
|
||||
if monthlyProducer != nil {
|
||||
rabbitMQConsumers = rabbitmqconsumer.NewWithPackageCustomerSyncAndMonthlyUsage(trafficUsage, workOrder, balance, packageCustomerSync, monthlyProducer, batchRuntime)
|
||||
} else {
|
||||
rabbitMQConsumers = rabbitmqconsumer.NewWithPackageCustomerSync(trafficUsage, workOrder, balance, packageCustomerSync, batchRuntime)
|
||||
}
|
||||
if statusProducer != nil {
|
||||
rabbitMQConsumers.WithSimStatusQueue(statusProducer, statusProducer)
|
||||
}
|
||||
if dailyProducer != nil {
|
||||
rabbitMQConsumers.WithDailyUsageStart(dailyProducer)
|
||||
}
|
||||
if monthlyHistoryProducer != nil {
|
||||
rabbitMQConsumers.WithMonthlyHistoryStart(monthlyHistoryProducer)
|
||||
}
|
||||
if imeiProducer != nil {
|
||||
rabbitMQConsumers.WithImeiQueue(imeiProducer)
|
||||
}
|
||||
var (
|
||||
fullSyncHTTP *httpapi.Server
|
||||
fullSyncArchives []*fullsyncarchive.Service
|
||||
)
|
||||
{ // The authenticated control plane is always available.
|
||||
prefixes, err := loadFullSyncArchivePrefixes(workerCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stores := make([]*zset.Store, 0, len(prefixes))
|
||||
for _, prefix := range prefixes {
|
||||
store, e := zset.New(g.Redis(), prefix)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
stores = append(stores, store)
|
||||
}
|
||||
archiveRoot := g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.rootDir", "/app/logs/full-sync").String()
|
||||
repositories := make([]*fullsyncruntime.Repository, 0, len(stores))
|
||||
for _, store := range stores {
|
||||
repo, e := fullsyncruntime.New(store, archiveRoot)
|
||||
if e != nil {
|
||||
return gerror.Wrap(e, "initialize full-sync runtime repository failed")
|
||||
}
|
||||
repositories = append(repositories, repo)
|
||||
}
|
||||
catalog, err := fullsyncruntime.NewCatalog(repositories...)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize full-sync run catalog failed")
|
||||
}
|
||||
simple, err := fullsyncruntime.NewSimpleReplayWithDefault(g.Cfg().MustGet(workerCtx, "fullSyncOps.replay.maxRounds", fullsyncruntime.DefaultReplayMaxRounds).Int(), stores...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Only Admin exposes replay to users. This listener stays inside the
|
||||
// Docker network; the Worker service must not publish port 18081.
|
||||
fullSyncHTTP, err = httpapi.NewServer(httpapi.Config{Addr: ":18081", ShutdownTimeout: g.Cfg().MustGet(workerCtx, "fullSyncOps.control.shutdownTimeout", "10s").Duration()}, httpapi.Dependencies{Runs: catalog, List: catalog, Failures: catalog, Replays: repositories[0], Actions: repositories[0], Simple: simple})
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "initialize full-sync control server failed")
|
||||
}
|
||||
if err := fullSyncHTTP.Start(workerCtx); err != nil {
|
||||
return gerror.Wrap(err, "start full-sync control server failed")
|
||||
}
|
||||
simple.Start(workerCtx)
|
||||
}
|
||||
if g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.enabled", false).Bool() {
|
||||
prefixes, err := loadFullSyncArchivePrefixes(workerCtx)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "load full-sync archive Redis prefixes failed")
|
||||
}
|
||||
for _, prefix := range prefixes {
|
||||
store, err := zset.New(g.Redis(), prefix)
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err, "initialize full-sync archive Redis store failed for prefix %q", prefix)
|
||||
}
|
||||
store.EnableArchiveIndex(true)
|
||||
repo, err := fullsyncruntime.New(store, g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.rootDir", "/app/logs/full-sync").String())
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err, "initialize full-sync archive repository failed for prefix %q", prefix)
|
||||
}
|
||||
service := fullsyncarchive.NewService(fullsyncarchive.Config{Root: g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.rootDir", "/app/logs/full-sync").String(), Interval: g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.pollInterval", "10s").Duration(), InitialBackoff: g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.retryMin", "5s").Duration(), MaxBackoff: g.Cfg().MustGet(workerCtx, "fullSyncOps.archive.retryMax", "5m").Duration()}, repo, repo, repo)
|
||||
if err := service.Start(workerCtx); err != nil {
|
||||
return gerror.Wrapf(err, "start full-sync archive service failed for prefix %q", prefix)
|
||||
}
|
||||
fullSyncArchives = append(fullSyncArchives, service)
|
||||
}
|
||||
}
|
||||
batchDispatcher.Register(consts.BatchTypeInbound, inboundExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeAllocate, allocateExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeCardAddPackage, addPackageCardExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypePoolAddPackage, addPackagePoolExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypePrepare, prepareExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeRenew, renewExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeChange, changeExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeDataCut, dataExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeDataStart, dataExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeDataStop, dataExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeSimCardUnsub, unsubExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeSimCardRestart, restartExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeSimCardCancel, cancelExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeSimWarningConfig, warningConfigExecutor)
|
||||
batchDispatcher.Register(consts.BatchTypeTrafficCutoffConfig, trafficCutoffConfigExecutor)
|
||||
if err := rabbitMQConsumers.Start(workerCtx); err != nil {
|
||||
_ = rabbitMQConsumers.Stop(context.Background())
|
||||
return gerror.Wrap(err, "start RabbitMQ consumers failed")
|
||||
}
|
||||
|
||||
g.Log().Info(workerCtx, "iotlcm worker started")
|
||||
<-workerCtx.Done()
|
||||
g.Log().Info(ctx, "iotlcm worker stopping")
|
||||
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||
defer cancel()
|
||||
if err := rabbitMQConsumers.Stop(shutdownCtx); err != nil {
|
||||
return gerror.Wrap(err, "stop RabbitMQ consumers failed")
|
||||
}
|
||||
for _, service := range fullSyncArchives {
|
||||
if err := service.Stop(); err != nil {
|
||||
return gerror.Wrap(err, "stop full-sync archive service failed")
|
||||
}
|
||||
}
|
||||
if fullSyncHTTP != nil {
|
||||
if err := fullSyncHTTP.Wait(); err != nil {
|
||||
return gerror.Wrap(err, "stop full-sync control server failed")
|
||||
}
|
||||
}
|
||||
if err := tdengine.Close(); err != nil {
|
||||
return gerror.Wrap(err, "close TDengine failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initialize(ctx context.Context) error {
|
||||
if err := g.DB().PingMaster(); err != nil {
|
||||
return gerror.Wrap(err, "connect database failed")
|
||||
}
|
||||
if err := gtime.SetTimeZone("Asia/Shanghai"); err != nil {
|
||||
return gerror.Wrap(err, "set timezone failed")
|
||||
}
|
||||
|
||||
mode := g.Cfg().MustGet(ctx, "system.gfmode", gmode.NOT_SET).String()
|
||||
for _, supported := range []string{gmode.DEVELOP, gmode.TESTING, gmode.STAGING, gmode.PRODUCT} {
|
||||
if mode == supported {
|
||||
gmode.Set(mode)
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user