Quartz长耗时任务调度漏执行、重复执行问题咨询
Quartz调度长耗时任务出现跳执行、重复执行问题
问题基本情况
使用Quartz调度器处理单作业耗时3-5分钟的长耗时任务时,出现作业跳过、重复执行异常:
- 数据拆分规则:总数据量约14000行,按每个作业处理300行拆分,共拆分出47个作业
- 预期执行结果:各批次处理数据量序列为
300,300,300,...,102 - 实际执行结果:仅首个作业和最后一个作业的数据插入数据库,最后一个作业多次重复执行,实际数据量序列为
300,102,102,102
现有实现代码
作业类定义
作业类添加了[DisallowConcurrentExecution]、[PersistJobDataAfterExecution]特性,具体代码如下:
[DisallowConcurrentExecution] [PersistJobDataAfterExecution] public class UploadNumberData : IJob { private readonly IServiceProvider serviceProvider; public UploadNumberData(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; } public async Task Execute(IJobExecutionContext context) { var jobDataMap = context.MergedJobDataMap; string flattenedInput = jobDataMap.GetString("FlattenedInput"); string applicationName = jobDataMap.GetString("ApplicationName"); var parsedFlattenedInput = JsonSerializer.Deserialize<List<NumberDataUploadViewModel>>(flattenedInput); var parsedApplicationName = JsonSerializer.Deserialize<string>(applicationName); using (var scope = serviceProvider.CreateScope()) { // 执行数据上传处理逻辑 } } }
作业触发逻辑
接口拆分完数据后,循环为每个批次创建作业和触发器,核心代码如下:
try { var flattenedInput = JsonSerializer.Serialize(Input.NumData); var triggerKey = Guid.NewGuid().ToString(); IJobDetail job = JobBuilder.Create<UploadNumberData>() .UsingJobData("FlattenedInput", flattenedInput) .UsingJobData("ApplicationName", flattenedApplicationName) .StoreDurably() .WithIdentity("BatchNumberDataJob", $"GP_BatchNumberDataJob") .Build(); await scheduler.AddJob(job, true); ITrigger trigger = TriggerBuilder.Create() .ForJob(job) .WithIdentity(triggerKey, $"GP_BatchNumberDataJob") .WithSimpleSchedule(x => x.WithMisfireHandlingInstructionFireNow()) .StartNow() .Build(); await scheduler.ScheduleJob(trigger); } catch(Exception e) { // 记录异常日志 }
Quartz持久化配置
使用MySQL作为作业存储的持久化方案,配置如下:
NameValueCollection quartzProperties = new NameValueCollection { {"quartz.serializer.type","json" }, {"quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" }, {"quartz.jobStore.dataSource","default" }, {"quartz.dataSource.default.provider","MySql" }, {"quartz.dataSource.default.connectionString","connectionstring"}, {"quartz.jobStore.driverDelegateType","Quartz.Impl.AdoJobStore.MySQLDelegate, Quartz" }, {"quartz.jobStore.misfireThreshold","3600000" } };
排查过程
- 首次尝试修改Job标识名为不同值,作业执行时数据库插入操作触发外键错误
- 后续测试将调度线程数设置为1、Job标识改为动态生成值后,任务可正常执行
待确认疑问
将调度线程数设置为1、Job标识改为动态生成值的方案,是否会抵消[DisallowConcurrentExecution]特性的原有设计作用?
内容的提问来源于stack exchange,提问作者JianYA
相关产品推荐
相关产品推荐

