背景
当前 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 数量多”本身,而是缺少统一治理层:
-
生命周期分散
- 有的在 provider 里启动。
- 有的在 constructor 里启动。
- 有的由 timing wheel 注册。
- 有的是 cron scheduler。
- 有的是 lazy pool。
- cleanup 分散在 Wire cleanup 里,关闭顺序和超时不统一。
-
配置不统一
- 有些 interval / worker count / queue size 在 config 中。
- 有些是硬编码常量。
- 有些 enabled 开关和实际启动条件不一致。
- 有些 pool 支持 autoscale,有些固定数量。
-
观测不统一
- 缺少统一 worker 列表、状态、last run、next run、last error、queue depth、dropped count、duration、success/failure metrics。
- 排查线上卡顿时很难快速判断是哪个 worker 正在扫表、堆积或失败重试。
-
并发和资源预算不统一
- 多个 worker 可能同时触发 DB 重查询 / 聚合 / 清理。
- 缺少全局和分组级 concurrency budget。
- 缺少后台任务与在线请求之间的资源隔离策略。
-
多实例协调不统一
- 一些任务有 leader lock,一些没有。
- 分布式部署下哪些任务必须 singleton、哪些可以每实例运行,目前没有统一声明。
-
扩展风险
- 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。
第一阶段迁移范围
建议第一阶段只迁移/纳管这些高价值任务,不追求一次性全量:
usage_cleanup_service
dashboard_aggregation_service
ops_cleanup_service
ops_metrics_collector
ops_alert_evaluator_service
billing_cache_service async write pool
usage_record_worker_pool
- 即将新增的 usage / billing rollup worker
原因:这些任务要么会访问大表,要么 worker 数量大,要么和后续性能治理强相关。
非目标
- 不在第一阶段重写所有业务逻辑。
- 不强制所有一次性 goroutine 都接入 registry。
- 不把 HTTP server listener、请求级 goroutine、测试 goroutine 算作 worker registry 对象。
- 不替代现有 timing wheel / cron / pond,而是先统一封装和治理。
兼容迁移策略
- 新增
internal/worker 或 internal/runtime/worker 包。
- 先让 registry 包装现有 timing wheel / cron / pond,不直接大改业务代码。
- 为每个被纳管 worker 增加 descriptor:name、group、type、enabled、interval、singleton、resource profile。
- 在 Wire cleanup 中改为 registry 统一 stop,保留旧 Stop 调用直到迁移完成。
- 增加 admin/ops status endpoint 或内部 metrics dump。
- 后续新 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,可以避免后续继续扩大后台任务碎片化。
背景
当前 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。后续还要引入 usage / billing rollup worker、缓存刷新/失效 worker、日志降噪/聚合 worker。如果继续按现在的方式在各 service 里各自
go func/ ticker / cron / pool,会进一步放大生命周期、观测、配置和关闭顺序的复杂度。问题
目前后台任务体系的主要问题不是“worker 数量多”本身,而是缺少统一治理层:
生命周期分散
配置不统一
观测不统一
并发和资源预算不统一
多实例协调不统一
扩展风险
提议:引入统一 Worker Registry
新增一个项目内统一 worker registry / runtime,用于注册、启动、停止、观测和治理后台任务。
目标不是一次性重写所有 worker,而是先建立统一接口和 registry,再逐步迁移高风险/高资源消耗任务。
建议设计
Worker 定义
或按任务类型拆成:
Registry 能力
usagebillingopsauthschedulermaintenance第一阶段迁移范围
建议第一阶段只迁移/纳管这些高价值任务,不追求一次性全量:
usage_cleanup_servicedashboard_aggregation_serviceops_cleanup_serviceops_metrics_collectorops_alert_evaluator_servicebilling_cache_serviceasync write poolusage_record_worker_pool原因:这些任务要么会访问大表,要么 worker 数量大,要么和后续性能治理强相关。
非目标
兼容迁移策略
internal/worker或internal/runtime/worker包。go func+ ticker。验收标准
风险与注意事项
关联
这两个方向都会继续增加后台任务,例如 rollup worker、cache refresh/invalidation worker、日志聚合/降噪 worker。因此先统一 worker registry,可以避免后续继续扩大后台任务碎片化。