Skip to content

refactor: 统一后台 worker registry 与生命周期治理 #9

Description

@is7Qin

背景

当前 backend 已经存在大量后台 worker / scheduler / ticker / cron / worker pool,分散在 wire.go、各 service constructor、handler lazy pool 和 timing wheel 中。粗略盘点后,生产链路里已有 20+ 类后台任务/服务,另外还有多个固定或可配置的 worker pool。

典型例子:

  • usage_record_worker_pool:默认 128 workers,可 autoscale 到 512。
  • billing_cache_service:固定 10 个 async write workers。
  • email_queue_service:固定 3 个 workers。
  • channel_monitor_runner:pond pool 容量 5。
  • ops_error_logger:lazy pool,min 4 / max 32。
  • 多个 1 goroutine ticker/cron/scheduler:token refresh、account expiry、subscription expiry、payment order expiry、idempotency cleanup、ops metrics、ops alert、ops scheduled reports、ops system log sink、scheduler snapshot、usage cleanup、dashboard aggregation、backup cron 等。

后续还要引入 usage / billing rollup worker、缓存刷新/失效 worker、日志降噪/聚合 worker。如果继续按现在的方式在各 service 里各自 go func / ticker / cron / pool,会进一步放大生命周期、观测、配置和关闭顺序的复杂度。

问题

目前后台任务体系的主要问题不是“worker 数量多”本身,而是缺少统一治理层:

  1. 生命周期分散

    • 有的在 provider 里启动。
    • 有的在 constructor 里启动。
    • 有的由 timing wheel 注册。
    • 有的是 cron scheduler。
    • 有的是 lazy pool。
    • cleanup 分散在 Wire cleanup 里,关闭顺序和超时不统一。
  2. 配置不统一

    • 有些 interval / worker count / queue size 在 config 中。
    • 有些是硬编码常量。
    • 有些 enabled 开关和实际启动条件不一致。
    • 有些 pool 支持 autoscale,有些固定数量。
  3. 观测不统一

    • 缺少统一 worker 列表、状态、last run、next run、last error、queue depth、dropped count、duration、success/failure metrics。
    • 排查线上卡顿时很难快速判断是哪个 worker 正在扫表、堆积或失败重试。
  4. 并发和资源预算不统一

    • 多个 worker 可能同时触发 DB 重查询 / 聚合 / 清理。
    • 缺少全局和分组级 concurrency budget。
    • 缺少后台任务与在线请求之间的资源隔离策略。
  5. 多实例协调不统一

    • 一些任务有 leader lock,一些没有。
    • 分布式部署下哪些任务必须 singleton、哪些可以每实例运行,目前没有统一声明。
  6. 扩展风险

    • usage/billing rollup、日志聚合、缓存刷新等新 worker 如果继续散落实现,会让后台任务体系越来越难维护。

提议:引入统一 Worker Registry

新增一个项目内统一 worker registry / runtime,用于注册、启动、停止、观测和治理后台任务。

目标不是一次性重写所有 worker,而是先建立统一接口和 registry,再逐步迁移高风险/高资源消耗任务。

建议设计

Worker 定义

type Worker interface {
    Name() string
    Start(ctx context.Context) error
    Stop(ctx context.Context) error
    Status(ctx context.Context) WorkerStatus
}

或按任务类型拆成:

type PeriodicJob struct {
    Name        string
    Interval    time.Duration
    Timeout     time.Duration
    Jitter      time.Duration
    Singleton   bool
    Group       string
    Run         func(context.Context) error
}

type QueueWorkerPool struct {
    Name        string
    WorkerCount int
    QueueSize   int
    Timeout     time.Duration
    Group       string
}

type CronJob struct {
    Name      string
    Schedule  string
    Singleton bool
    Timeout   time.Duration
    Run       func(context.Context) error
}

Registry 能力

  • 注册 worker / periodic job / cron job / worker pool。
  • 统一 StartAll / StopAll,支持关闭超时和依赖顺序。
  • 统一 panic recovery、backoff、run timeout。
  • 统一 leader lock / singleton 声明。
  • 统一 metrics:run count、success/failure、duration、last error、queue depth、worker count、dropped count。
  • 统一 health/status API,便于 admin/ops 查看后台任务状态。
  • 统一资源分组,例如:
    • usage
    • billing
    • ops
    • auth
    • scheduler
    • maintenance
  • 支持全局和 group 级 concurrency budget,避免后台任务同时打爆 DB。

第一阶段迁移范围

建议第一阶段只迁移/纳管这些高价值任务,不追求一次性全量:

  1. usage_cleanup_service
  2. dashboard_aggregation_service
  3. ops_cleanup_service
  4. ops_metrics_collector
  5. ops_alert_evaluator_service
  6. billing_cache_service async write pool
  7. usage_record_worker_pool
  8. 即将新增的 usage / billing rollup worker

原因:这些任务要么会访问大表,要么 worker 数量大,要么和后续性能治理强相关。

非目标

  • 不在第一阶段重写所有业务逻辑。
  • 不强制所有一次性 goroutine 都接入 registry。
  • 不把 HTTP server listener、请求级 goroutine、测试 goroutine 算作 worker registry 对象。
  • 不替代现有 timing wheel / cron / pond,而是先统一封装和治理。

兼容迁移策略

  1. 新增 internal/workerinternal/runtime/worker 包。
  2. 先让 registry 包装现有 timing wheel / cron / pond,不直接大改业务代码。
  3. 为每个被纳管 worker 增加 descriptor:name、group、type、enabled、interval、singleton、resource profile。
  4. 在 Wire cleanup 中改为 registry 统一 stop,保留旧 Stop 调用直到迁移完成。
  5. 增加 admin/ops status endpoint 或内部 metrics dump。
  6. 后续新 worker 必须通过 registry 注册,不再散落 go func + ticker。

验收标准

  • 新增统一 worker registry 抽象和实现。
  • 至少纳管 3 个现有后台任务,其中至少包含一个 periodic job 和一个 worker pool。
  • registry 能列出所有已纳管 worker 的:
    • name
    • type
    • group
    • enabled
    • running/stopped
    • last run time
    • last error
    • success/failure count
    • average/last duration
  • 支持统一 graceful shutdown,带超时和错误汇总。
  • 支持 panic recovery,单个 worker panic 不导致整个进程崩溃。
  • 对 singleton job 提供统一 leader lock 适配,避免每个 service 自己写。
  • 有单元测试覆盖:注册、启动、停止、重复注册、panic recovery、timeout、status 更新。
  • 文档列出当前已纳管和未纳管 worker 清单。

风险与注意事项

  • 不要为了 registry 一次性重构所有 worker,避免大范围回归。
  • registry 不应隐藏任务失败;错误必须可观测、可告警。
  • 对 usage/billing/ops 这类 DB-heavy worker,要引入并发预算或至少预留接口。
  • 多实例部署下 singleton 语义必须明确:每实例执行 vs 全局单实例执行。
  • registry 应该是治理层,不应该承载具体业务逻辑。

关联

这两个方向都会继续增加后台任务,例如 rollup worker、cache refresh/invalidation worker、日志聚合/降噪 worker。因此先统一 worker registry,可以避免后续继续扩大后台任务碎片化。

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureArchitecture and system design changesenhancementNew feature or requestperformancePerformance, scalability, and database load improvements

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions