You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 16:15:32