.NET 8中Quartz.NET非生产环境一次性作业触发失败问题
使用.NET 8搭建Quartz Job时,需求如下:
- 生产环境通过Cron表达式调度作业
- 非生产(开发、本地等)环境仅注册作业不调度,通过Controller端点触发一次性作业
已配置的代码如下:
Startup.cs 配置
var environmentType = Environment.Type; services.AddQuartz(); services.AddQuartzHostedService(opt => { opt.WaitForJobsToComplete = true; }); if (Environment.Type == EnvironmentType.Production) { services.ConfigureOptions<ProcessIssuersJobSetupWithCron>(); } else if (environmentType is EnvironmentType.TestingRc or EnvironmentType.TestingMaster or EnvironmentType.Staging or EnvironmentType.Local) { services.ConfigureOptions<ProcessIssuersJobSetupWithoutCron>(); }
非生产环境作业配置类 ProcessIssuersJobSetupWithoutCron
public class ProcessIssuersJobSetupWithoutCron : IConfigureOptions<QuartzOptions> { public void Configure(QuartzOptions options) { var processIssuersJobKey = JobKey.Create("ProcessIssuersJob"); options .AddJob<ProcessIssuersJob>(jobBuilder => jobBuilder.WithIdentity(processIssuersJobKey)) .AddTrigger(trigger => trigger .ForJob(jobKey: processIssuersJobKey)); } }
Controller触发代码
public class QuartzController(ISchedulerFactory scheduleFactory) : Controller { private readonly ISchedulerFactory _scheduleFactory = scheduleFactory; [HttpPost("ProcessIssuers")] public async Task<IActionResult> ExecuteDataStoredProcedures([FromQuery] DateTime runDate) { var scheduler = await this._scheduleFactory.GetScheduler(); var jobKey = JobKey.Create("ProcessIssuersJob"); var jobDataMap = new JobDataMap(); jobDataMap.Put("runDate", runDate); await scheduler.TriggerJob(jobKey, jobDataMap); return this.Ok(); } }
执行await scheduler.TriggerJob(jobKey, jobDataMap)时出现错误:
Exception Type: Quartz.JobPersistenceException
Error message: The job (DEFAULT.ProcessIssuersJob) referenced by the trigger does not exist.
调试确认ProcessIssuersJobSetupWithoutCron的Configure方法已执行,且JobKey与触发时一致为DEFAULT.ProcessIssuersJob,生产环境的Cron配置下一次性作业可正常触发,问题出在哪里?
核心原因
在非生产环境的配置中,你只通过AddTrigger关联了触发器和作业,但没有给触发器设置任何触发规则,导致Quartz不会将该作业持久化到调度器的作业存储中。而生产环境的Cron配置中,触发器有明确的Cron表达式,Quartz会自动完成作业的注册与绑定。
解决步骤
方案1:移除无效的触发器配置,仅注册作业
修改ProcessIssuersJobSetupWithoutCron,只添加作业,不添加无触发规则的触发器:
public class ProcessIssuersJobSetupWithoutCron : IConfigureOptions<QuartzOptions> { public void Configure(QuartzOptions options) { var processIssuersJobKey = JobKey.Create("ProcessIssuersJob"); // 仅注册作业,不添加触发器 options.AddJob<ProcessIssuersJob>(jobBuilder => jobBuilder.WithIdentity(processIssuersJobKey)); } }
方案2:给触发器设置一个永远不会触发的规则(保留触发器结构)
如果需要保留触发器的配置结构,可以给触发器设置一个过去的时间作为触发时间,确保它不会自动执行,但能让Quartz完成作业的注册:
public class ProcessIssuersJobSetupWithoutCron : IConfigureOptions<QuartzOptions> { public void Configure(QuartzOptions options) { var processIssuersJobKey = JobKey.Create("ProcessIssuersJob"); options .AddJob<ProcessIssuersJob>(jobBuilder => jobBuilder.WithIdentity(processIssuersJobKey)) .AddTrigger(trigger => trigger .ForJob(jobKey: processIssuersJobKey) // 设置一个过去的时间,确保不会自动触发 .StartAt(DateTimeOffset.UtcNow.AddDays(-1))); } }
验证逻辑
当你调用TriggerJob时,Quartz需要确认作业已经在调度器中存在。如果只添加了无触发规则的触发器,Quartz不会将作业正式注册到调度器的作业存储中,导致触发时提示作业不存在。移除无效触发器或给触发器设置有效(但不自动执行)的规则后,作业会被正确注册,就能通过TriggerJob正常触发。
内容的提问来源于stack exchange,提问作者Manuel Raso

