
任务调度后端【免费下载链接】quartznetQuartz Enterprise Scheduler .NET项目地址https://gitcode.com/gh_mirrors/qu/quartznet点击查看免费下载本篇技术指南以 Quartz.NETQuartz Enterprise Scheduler .NET官方文档 one-off-job.md 为骨架系统讲解如何用 Quartz.NET 调度只运行一次的任务先注册、后按需触发TriggerJob以及运行时动态创建作业 触发器即刻或定时触发。文中会结合仓库内 IScheduler.cs、QuartzScheduler.cs、SchedulerJobExtensions.cs 等核心实现与单元测试帮助你理解每种方式的底层行为、适用场景与注意事项并给出可直接复制运行、可对照源码验证的 C# 代码。什么是 One-Off JobQuartz.NET 中的 One-Off Job一次性作业指只执行一次、执行后不再重复的任务。它没有 cron 表达式也没有固定重复次数通常用于用户点击立即运行按钮后立刻执行的异步操作延迟到某个时间点执行的提醒、通知、超时清理在消息处理流程中安排下一个步骤如续作、重试、超时补偿数据导入、报表生成等一次性批处理。官方文档给出的一次性作业默认行为是Misfire Mode失火策略为 Smart。对于只触发一次的 SimpleTriggerSmartPolicy会被解析为FireNow立即补火也就是说——如果调度器在任务应触发时处于停机状态错过的那一次触发会在调度器恢复后立即执行而不是被丢弃。这一行为在 SimpleTriggerMisfireInstruction.cs 中有明确的注释依据Intended for one-shot (non-repeating) triggers。方式一提前注册作业Ahead of Time之后按需触发适用场景作业集合相对固定你希望在应用启动时就把它们注册到调度器里之后在任意时刻例如某个 API 被调用时触发它们运行。注册一个休眠的持久作业public async Task DoSomething(IScheduler scheduler, CancellationToken ct) { var job JobBuilder.CreateAnExampleJob() .WithIdentity(name, group) .Build(); var replace true; var durable true; await scheduler.AddJob(job, replace, durable, ct); }这段代码对应的接口签名为IScheduler.AddJob(IJobDetail jobDetail, bool replace, bool durable, CancellationToken ct)3.x 重载另见 IScheduler.cs 中 4.x 的AddJob(IJobDetail, AddJobOptions, CancellationToken)durable true持久作业即使没有关联任何触发器也会一直保存在 JobStore 中保持休眠状态直到它被调度或手动触发。非持久作业在没有触发器时会被自动删除。replace true允许用同名name group作业覆盖已经存储的旧作业。若replace false而键已存在会抛出ObjectAlreadyExistsException。从源码看AddJob在 QuartzScheduler.cs 的实现注释明确说明The job will be dormant until it is scheduled with a trigger, orTriggerJobis called for it且作业必须是 durable除非设置了AddJobOptions.StoreNonDurableWhileAwaitingScheduling否则会抛出SchedulerException。用 TriggerJob 立即触发注册之后在任意位置通过JobKey找到并触发它public async Task DoSomething(IScheduler scheduler, CancellationToken ct) { await scheduler.TriggerJob(new JobKey(name, group), ct); }TriggerJob只会让作业立即执行一次并且不留任何痕迹——它内部创建一个只触发一次repeatCount 0的临时 SimpleTrigger触发完成后即被移除不会在调度器里留下周期性调度。底层实现见 QuartzScheduler.cs该方法用NewTriggerId()生成触发器键ComputeFirstFireTimeUtc计算触发时间后写入 JobStore再通过NotifySchedulerThread唤醒调度线程去取触发。给本次触发携带 JobDataMapTriggerJob的重载允许传入一个JobDataMap这些数据只对本次触发有效并会与作业自身的 JobDataMap 合并public async Task DoSomething(IScheduler scheduler, CancellationToken ct) { var jobData new JobDataMap(); await scheduler.TriggerJob(new JobKey(name, group), jobData, ct); }// 更常见的用法把业务参数放进本次触发 var jobData new JobDataMap { { CustomerId, cust-1234 } }; await scheduler.TriggerJob(new JobKey(name, group), jobData, ct);从 QuartzScheduler.cs 的实现可以看到传入的data会被直接挂到临时触发器的JobDataMap上并且无论是否传数据都会调用PrepareTriggerData对数据做统一序列化预处理——保证数据以字符串形式进入 JobStore实现跨存储、跨序列化的可移植性。小结方式一的取舍维度说明适合场景作业类型固定、需要从多处按需触发、触发时携带动态参数作业生命周期持久durable即使无触发器也保留触发成本每次TriggerJob内部创建一个一次性触发器触发后即焚前提作业必须 durable或使用StoreNonDurableWhileAwaitingScheduling方式二动态注册Dynamic Registration当场创建作业与触发器适用场景作业集合是动态的——你事先不知道会有哪些作业、何时触发需要在运行时现场创建作业和触发器并立刻调度。public async Task DoSomething(IScheduler scheduler, CancellationToken ct) { var job JobBuilder.CreateAnExampleJob() .WithIdentity(name, group) .Build(); var trigger TriggerBuilder.Create() .WithIdentity(name, group) .StartNow() .Build(); await scheduler.ScheduleJob(job, trigger, ct); }这段代码等价于下面这种显式声明简单调度器的写法文档明确说明 The above is the same aspublic async Task DoSomething(IScheduler scheduler, CancellationToken ct) { var job JobBuilder.CreateAnExampleJob() .WithIdentity(name, group) .Build(); var trigger TriggerBuilder.Create() .WithIdentity(name, group) .WithSimpleSchedule() .StartNow() .Build(); await scheduler.ScheduleJob(job, trigger, ct); }关键点不调用WithSimpleSchedule()时TriggerBuilder 默认构建的其实就是无重复、到点触发一次的 SimpleTrigger显式加一个空的.WithSimpleSchedule()只是为了后续能配置 repeat 次数、间隔或 misfire 指令二者行为一致。.StartNow()表示从现在起尽快触发由调度线程的轮询节奏决定通常是毫秒级延迟。若想指定时间点改用.StartAt(DateTimeOffset)。由于作业与触发器一起通过ScheduleJob(job, trigger)存储不需要 durable两者会在触发器完成使命触发一次且无剩余工作后一起被自动移除。从接口签名看ScheduleJob(IJobDetail, ITrigger)返回ValueTaskDateTimeOffset首次触发时间并支持ScheduleJobOptions见 IScheduler.cs默认Replace false即键已存在时抛ObjectAlreadyExistsException置Replace true则可实现一次调用、单锁内原子替换无需自己编排CheckExists→UnscheduleJob→ScheduleJob三步三步走既慢又存在与其他节点竞争竞态的风险。相关语义在 ScheduleJobOptions.cs 中有完整注释。小结方式二的取舍维度说明适合场景作业触发器都动态生成例如用户自定义的一次性任务作业生命周期非持久触发器执行完后一起被移除触发方式StartNow()立即 /StartAt()定时副作用若键冲突默认抛异常可用ScheduleJobOptions.Replace原子替换失火Misfire行为One-Off 任务的补火语义文档开头以:::tip形式强调One-Off Job 的失火模式是Smart。这里结合 SimpleTriggerMisfireInstruction.cs 把语义讲透SmartPolicy默认值由调度器根据触发器的 repeat 次数和间隔自行选择策略。对一次性非重复触发器解析结果等价于FireNow——错过即补。FireNow立即补火。官方注释明确Intended for one-shot (non-repeating) triggers用于一次性触发器最合适。IgnoreMisfires不把错过的触发视为失火尽快触发并继续仿佛按时发生。其他策略如NowWithRemainingCount、NextWithRemainingCount等主要面向重复触发器一次性任务用不到。实际效果如果调度器在触发时刻处于停机/待机状态恢复后该一次性任务会被补执行而不是被静默丢弃。若希望错过就算了可以显式配置合适的 misfire 指令例如var trigger TriggerBuilder.Create() .WithIdentity(name, group) .StartNow() .WithSimpleSchedule(x x .WithMisfireInstruction(SimpleTriggerMisfireInstruction.FireNow)) .Build();想深入了解 SimpleTrigger 各失火策略的完整定义可继续阅读仓库文档 SimpleTriggers若使用 4.x 版本则见 simpletriggers.md。一次性任务的进阶形态4.x 单行 API源码级解读3.x 文档聚焦前两种基础方式当前仓库的 4.x 版本在 one-off-job.md 中对同一主题做了大幅演进出现了一个载荷 一个时间一次调用的强类型单行 API。虽然本指南以 3.x 文档为主体这里结合源码做必要的纵向补充方便迁移读者对照。ScheduleJobTJob, TInput单行调度public sealed record SendInvoice(string CustomerId, decimal Amount); public sealed class SendInvoiceJob : IJobSendInvoice { public ValueTask Execute(IJobExecutionContext context, SendInvoice input, CancellationToken cancellationToken default) { // input.CustomerId, input.Amount return default; } } public async ValueTask Remind(IScheduler scheduler, ILogger logger, SendInvoice invoice, CancellationToken cancellationToken) { ScheduledOneOffJob firing await scheduler.ScheduleJobSendInvoiceJob, SendInvoice( invoice, TimeSpan.FromDays(7), OneOffJobOptions.Replacing($invoice-{invoice.CustomerId}) with { Group invoice.CustomerId }, cancellationToken); logger.LogInformation(Reminder {Trigger} scheduled for {At}, firing.TriggerKey, firing.FirstFireTimeUtc); // 取消本次触发 await scheduler.UnscheduleJob(firing.TriggerKey, cancellationToken); }该 API 的实现位于 SchedulerJobExtensions.cs其设计模型是每个作业类型一个持久作业每次调用一个触发器作业以SchedulerConstants.ScheduledJobKeyTJob()即(typeof(TJob).Name, QRTZ_SCHEDULED)见 SchedulerConstants.cs在第一次调用时以AddJobOptions.Replacing幂等存储一次并通过ConcurrentDictionary按调度器实例记忆后续调用省去一次往返——这在集群中多节点同时调用也是安全的。返回的ScheduledOneOffJob见 ScheduledOneOffJob.cs包含TriggerKey取消/替换的句柄与FirstFireTimeUtc存储计算出的首次触发时间与直接ScheduleJob(ITrigger)的返回值一致。OneOffJobOptions见 OneOffJobOptions.cs承载触发器设置Name默认生成的 GUID、Group默认取作业类型名、Description、Priority、ExecutionGroup、MisfireInstruction、Replace与RequestRecovery。其中Group默认是作业类型名而不是TriggerKey.DefaultGroup——如果既有代码用new TriggerKey(id)取消它指向默认组会对不上迁移时务必显式设置Group TriggerKey.DefaultGroup。支持DateTimeOffset定时、TimeSpan从现在延迟、Continuation.After(...)等待另一次触发完成后再执行即作业续作三类时间参数最后一个配合 Continuation 使用。若作业在单行调度前已存在而你想给同一个作业挂上自己的重复调度如 cron可以指向SchedulerConstants.ScheduledJobKeyTJob()而不是在保留组里再建一个作业。批量取消按组撤销整条业务关联public async Taskint CustomerWentAway(IScheduler scheduler, string customerId, CancellationToken cancellationToken) { ListTriggerKey calledOff await scheduler.UnscheduleJobs( GroupMatcherTriggerKey.GroupEquals(customerId), cancellationToken); return calledOff.Count; // 返回实际撤销的数量 }UnscheduleJobs(GroupMatcherTriggerKey)在存储层锁内一次性移除匹配组的所有触发器并返回被移除的键期间其他节点刚加进来的触发也会一并移除。注意不要用DeleteJobs(GroupMatcherJobKey)来取消整条关联因为单行 API 的持久作业是所有同类型触发共享的。源码证据与测试验证仓库中与本主题直接对应的实现与测试可以作为你继续深入阅读的入口接口契约IScheduler.AddJob / TriggerJob / ScheduleJob及完整异常语义见 IScheduler.cs。核心实现TriggerJob的一次性触发器构造、AddJob的 dormant 语义、PrepareTriggerData的输入序列化见 QuartzScheduler.cs。单行 APIScheduleJobTJob, TInput的 EnsureJob/建触发器/替换逻辑见 SchedulerJobExtensions.cs。选项类型AddJobOptionsAddJobOptions.cs、ScheduleJobOptionsScheduleJobOptions.cs、OneOffJobOptionsOneOffJobOptions.cs、返回类型 ScheduledOneOffJob.cs。单元测试TheOneLinerStoresOneDurableJobAndOneTriggerPerCall、TheOneLinerReplacesAFiringOfTheSameName等见 SchedulerTest.cs一个持久作业挂数千触发器的规模与持久性语义测试见 RAMJobStoreOneOffScaleTest.cs。可直接运行的示例代码文档配套样本 OneOffJobSamples.csWolverine 集成示例见 Part2OneOffFromHandler.cs 与 README.md。总结与选择建议作业固定、多处按需触发→ 方式一启动时AddJob(job, replace: true, durable: true)运行时TriggerJob(jobKey, data)。作业动态、即刻/定时触发一次→ 方式二JobBuilder.CreateT() TriggerBuilder.Create().StartNow()/StartAt(...)再ScheduleJob(job, trigger)。强类型载荷 延迟 可取消/可替换→ 4.x 单行 APIScheduleJobTJob, TInput(input, delay, OneOffJobOptions)用返回的TriggerKey取消。错过补火语义One-Off 默认 Smart → 对一次性触发等价于 FireNow调度器恢复后补执行需要错过即弃请显式配置 misfire 指令。无论选择哪种方式都建议基于 IScheduler.cs 的接口契约与 QuartzScheduler.cs 的实现确认异常与生命周期语义再结合 OneOffJobSamples.cs 的样例代码快速落地。赞分享任务调度后端【免费下载链接】quartznetQuartz Enterprise Scheduler .NET项目地址https://gitcode.com/gh_mirrors/qu/quartznet点击查看免费下载相关推荐Quartz.NET 任务重调度完全指南从 JobExecutionException 到自调度/自取消的四种实战方案Quartz.NET 任务重调度完全指南从 JobExecutionException 到自调度/自取消的四种实战方案 当一次任务执行失败、被限流或需要延后处任务调度后端KubeVela task 组件类型实战用 Application 声明式调度一次性 Job 任务KubeVela task 组件类型实战用 Application 声明式调度一次性 Job 任务 在 KubeVela 中 task 是内置的组件类型之一云原生DevOps运维微服务Quartz.NET 任务调度实战任务重调度策略详解Quartz.NET 任务调度实战任务重调度策略详解 前言 在分布式系统和后台任务处理中任务调度是一个核心组件。Quartz.NET作为.NET平台下功能强任务调度后端上一篇推荐开源项目AutoPkg - 自动化macOS软件打包神器下一篇LottieXamarin让动画设计与开发无缝衔接创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考