跳到主要内容

Cron Jobs

VEF 将 github.com/coldsmirk/vef-framework-go/cron 暴露为 gocron 之上的类型化包装。框架 DI 模块会自动提供 cron.Scheduler;需要自定义装配时,可以把自己的 gocron.Scheduler 传给 cron.NewScheduler

公开 API 面

该包没有 exported fields。公开 top-level API 如下:

API契约
cron.SchedulerDI、自定义 handler 和模块使用的高层 scheduler interface
cron.NewScheduler(scheduler gocron.Scheduler) cron.Scheduler包装调用方提供的 gocron.Scheduler;调用方必须传入可用 scheduler
cron.JobScheduler.NewJobScheduler.JobsScheduler.Update 返回的接口
cron.JobDefinitionScheduler.NewJobScheduler.Update 接收的接口
cron.JobDescriptorOption所有内置 job-definition 构造器接收的 option 类型
cron.OneTimeJobDefinitionNewOneTimeJob 返回的一次性 job 定义
cron.DurationJobDefinitionNewDurationJob 返回的固定间隔 job 定义
cron.DurationRandomJobDefinitionNewDurationRandomJob 返回的随机间隔 job 定义
cron.CronJobDefinitionNewCronJob 返回的 cron 表达式 job 定义
cron.NewOneTimeJob(times []time.Time, options ...cron.JobDescriptorOption) *cron.OneTimeJobDefinitiontimes 为 nil/空时立即执行一次;一个时间点执行一次;多个时间点各执行一次
cron.NewDurationJob(interval time.Duration, options ...cron.JobDescriptorOption) *cron.DurationJobDefinition按固定 duration 间隔重复执行
cron.NewDurationRandomJob(minInterval time.Duration, maxInterval time.Duration, options ...cron.JobDescriptorOption) *cron.DurationRandomJobDefinitionminIntervalmaxInterval 之间随机选择间隔重复执行
cron.NewCronJob(expression string, withSeconds bool, options ...cron.JobDescriptorOption) *cron.CronJobDefinition使用 cron 表达式;withSeconds=true 表示表达式带 seconds 字段,false 表示标准 5-field 格式
cron.WithName(name string)设置必需的人类可读 job name
cron.WithTags(tags ...string)设置 RemoveByTagsJob.Tags 使用的 tags
cron.WithConcurrent()允许同一个 job 重叠执行;未设置时使用 singleton wait mode
cron.WithStartAt(startAt time.Time)在指定时间开始调度;优先级高于 WithStartImmediately
cron.WithStartImmediately()仅当没有设置 WithStartAt 的非零时间时立即开始
cron.WithStopAt(stopAt time.Time)在指定时间停止调度
cron.WithLimitedRuns(limitedRuns uint)仅当 limitedRuns > 0 时设置运行次数限制
cron.WithContext(ctx context.Context)将非 nil context 传给底层 job option,用于取消支持
cron.WithTask(handler any, params ...any)设置必需的函数 handler,并通过 gocron.NewTask 转发 params
cron.ErrJobNameRequired构建 job option 时没有 name
cron.ErrJobTaskHandlerRequired构建 task 时 handler 为 nil
cron.ErrJobTaskHandlerMustFunc构建 task 时 handler 不是函数

cron.Scheduler

公开 scheduler interface 包含:

方法签名契约
JobsJobs() []cron.Job返回 wrapped scheduler 当前注册的 jobs
NewJobNewJob(definition cron.JobDefinition) (cron.Job, error)构建 definition、注册 job 并返回 cron.Job;definition validation error 会先于注册返回
RemoveByTagsRemoveByTags(tags ...string)删除拥有任一指定 tag 的 job
RemoveJobRemoveJob(id string) error先把 id 解析为 UUID,再删除该 job;非 UUID 字符串会在 delegation 前返回 parse error
StartStart()启动调度和执行;启动后新增的 job 由 wrapped scheduler 调度
StopJobsStopJobs() error停止 job 执行但不删除定义;之后可再次 Start()
UpdateUpdate(id string, definition cron.JobDefinition) (cron.Job, error)先把 id 解析为 UUID,再构建替换 definition,并保留 job identifier
JobsWaitingInQueueJobsWaitingInQueue() int返回 wrapped scheduler 的等待队列数量;在 wait mode 下才有实际意义

RemoveJobUpdate 应使用 Job.ID() 返回的 ID。任意字符串不会被接受。

cron.Job

每个已注册 job 都支持以下检查或触发方法:

方法签名契约
IDID() string返回底层 UUID 字符串
LastRunLastRun() (time.Time, error)返回最近一次 run 的开始时间
NameName() string返回配置的 job name
NextRunNextRun() (time.Time, error)返回下一次计划执行时间
NextRunsNextRuns(count int) ([]time.Time, error)返回 count 个未来计划执行时间
RunNowRunNow() error立即触发一次执行,同时仍遵守 job/scheduler 限制和运行次数限制
TagsTags() []string返回配置的 tag 列表

Job Definitions

cron.JobDefinitionScheduler.NewJob(...)Scheduler.Update(...) 接收的公共接口。四个 exported definition structs 由对应构造器返回;调用方通常不直接实例化它们,因为调度字段是内部字段。

构造器调度方式
cron.NewOneTimeJob(nil, options...)cron.NewOneTimeJob([]time.Time{}, options...)立即执行一次
cron.NewOneTimeJob([]time.Time{t}, options...)t 执行一次
cron.NewOneTimeJob([]time.Time{a, b}, options...)在每个给定时间点各执行一次
cron.NewDurationJob(interval, options...)固定间隔
cron.NewDurationRandomJob(minInterval, maxInterval, options...)给定范围内的随机间隔
cron.NewCronJob(expression, true, options...)带 seconds 字段的 cron 表达式
cron.NewCronJob(expression, false, options...)标准 5-field cron 表达式

所有内置 definition 都先构建 task,再构建 job options。因此如果 task handler 为 nil 或不是函数,同时 name 也缺失,调用方会先看到 task handler 相关错误。

Job Options

每个构造器都接收 JobDescriptorOption

Option行为
cron.WithName(name)必需;缺失时 NewJob/Update 返回包裹 ErrJobNameRequired 的 error
cron.WithTask(handler, params...)必需;handler 必须是函数;params 会转发给 gocron.NewTask(handler, params...)
cron.WithTags(tags...)保存 tags,用于列表展示和 RemoveByTags
cron.WithConcurrent()对该 job 禁用默认 singleton wait mode
cron.WithStartAt(startAt)设置开始 date/time;与 WithStartImmediately 同时出现时它优先
cron.WithStartImmediately()只有 WithStartAt 没有设置非零时间时才生效
cron.WithStopAt(stopAt)仅当 stopAt 非零时设置停止 date/time
cron.WithLimitedRuns(limitedRuns)仅当 limitedRuns > 0 时设置运行次数限制
cron.WithContext(ctx)仅当 ctx 非 nil 时添加 context option;接收 context.Context 的 handler 可感知取消

默认情况下,同一个 job 不会和自己的另一次执行重叠。除非提供 WithConcurrent(),否则包会添加 singleton wait mode。

错误哨兵

错误触发条件
cron.ErrJobTaskHandlerRequired未提供 WithTask,或 WithTask 收到 nil
cron.ErrJobTaskHandlerMustFuncWithTask 收到非函数 handler
cron.ErrJobNameRequiredtask 构建成功,但缺少 WithName 或 name 为空

这些 sentinel 会被 build path 包裹,例如 failed to build job task: ...failed to build job options: ...。判断时请使用 errors.Is(err, cron.ErrJobNameRequired) 等形式,不要直接用 ==

DI Scheduler 默认值

框架通过 DI module 创建 scheduler 时,可观察默认值如下:

默认值含义
本地时区调度使用 time.Local
停止超时 30s优雅关闭窗口
scheduler logger 和 monitorjob 调度与完成情况会记录日志
并发 job 上限 1000 且使用 wait mode超出的执行会进入队列等待,而不是被丢弃
lifecycle start/shutdown应用启动时 start scheduler;应用停止时 shutdown scheduler

这些默认值只适用于框架提供的 scheduler。传给 cron.NewScheduler 的自定义 scheduler 使用调用方自己的配置。

最小示例

package jobs

import (
"context"
"time"

"github.com/coldsmirk/vef-framework-go/cron"
)

func RegisterCleanupJob(scheduler cron.Scheduler) error {
_, err := scheduler.NewJob(
cron.NewDurationJob(
10*time.Minute,
cron.WithName("cleanup-expired-sessions"),
cron.WithTask(func(ctx context.Context) error {
return nil
}),
),
)

return err
}

实践场景

以下场景适合使用 cron:

  • 周期性清理
  • 轮询与同步任务
  • 定时聚合
  • 重试与超时扫描

如果任务本身业务逻辑很重,应把逻辑放进 service,让 scheduler 只负责编排。

下一步

继续阅读 事务,如果定时任务需要显式事务控制,就会接到那一层。