Azure多实例App Service中Quartz Job部署后重复执行问题
问题:Azure App Service多实例下Quartz部署后作业重复执行
我在.NET Core 6 Web API中使用Quartz.Extensions.Hosting 3.8.1调度包,部署环境是Azure App Service(2个实例)。应用运行基本正常,但每次部署后,作业会快速连续执行2-5次,执行间隔从几秒到几分钟不等,首次触发完成后后续都能按设定间隔正常运行。
例如:作业设定为1小时执行一次,部署后立即执行了4次(绿色标记),之后按1小时间隔正常执行(黄色标记)。
使用版本
- Quartz.Extensions.Hosting 3.8.1
代码实现
配置Quartz的扩展方法
public static void ConfigureQuartzJobs(this WebApplicationBuilder builder) { var services = builder.Services; services.AddQuartzHostedService(opt => opt.WaitForJobsToComplete = true); services.AddQuartz(q => { q.SchedulerId = "SomeId"; q.SchedulerName = "SomeName"; q.UsePersistentStore(s => { s.UseProperties = true; s.UseClustering(); s.UseSqlServer(connectionString); s.UseNewtonsoftJsonSerializer(); }); q.AddJob<job1>(jobkey1) .AddTrigger(trigger => trigger .ForJob(jobkey1) .WithIdentity(tr1) .StartAt(DateTimeOffset.UtcNow.AddSeconds(5)) .WithSimpleSchedule(schedule => schedule.WithIntervalInMinutes(5).RepeatForever())); }); } }
Job1实现类
[DisallowConcurrentExecution] public class Job1 : IJob { public async Task Execute(IJobExecutionContext context) { try { // 业务逻辑调用 } catch (Exception ex) { await Console.Out.WriteLineAsync($"{context.FireTimeUtc}, {ex}. FireInstanceId={context.FireInstanceId}"); } } }
已尝试的配置(未解决问题)
q.UseDefaultThreadPool(x => x.MaxConcurrency = 5); q.MisfireThreshold = TimeSpan.FromSeconds(60); // 触发器启动时间的不同设置 trigger.StartAt(DateTimeOffset.UtcNow.AddSeconds(5)) trigger.StartNow() // 不设置任何启动时间
期望行为
部署后,作业应仅执行一次或不执行,确保后续严格按触发器设定时间触发。
解决方案
1. 修正集群节点标识配置
硬编码的SchedulerId会导致多实例集群识别异常,改为自动生成唯一节点ID:
q.UsePersistentStore(s => { s.UseProperties = true; s.UseClustering(c => { // 让Quartz自动生成每个实例的唯一节点ID c.InstanceId = "AUTO"; }); s.UseSqlServer(connectionString); s.UseNewtonsoftJsonSerializer(); });
2. 调整触发器启动策略与错过触发处理
避免用当前时间计算启动点,改为基于固定时间偏移,同时明确错过触发的处理逻辑:
.AddTrigger(trigger => trigger .ForJob(jobkey1) .WithIdentity(tr1) // 部署后第一次触发在当前整点加5分钟,避免多实例同时计算"现在+5秒"导致重叠 .StartAt(DateTimeOffset.UtcNow.Round(TimeSpan.FromMinutes(1)).AddMinutes(5)) .WithSimpleSchedule(schedule => schedule .WithIntervalInMinutes(5) .RepeatForever() // 忽略错过的触发,避免部署重启后重复执行 .WithMisfireHandlingInstructionIgnoreMisfires()))
3. 调整MisfireThreshold阈值
将阈值设为更大的值,避免部署重启被误判为错过触发:
q.MisfireThreshold = TimeSpan.FromMinutes(2);
4. 检查实例时钟同步
在作业中输出实例UTC时间,确认多实例时钟是否一致:
await Console.Out.WriteLineAsync($"Instance UTC Time: {DateTime.UtcNow}, FireTimeUtc: {context.FireTimeUtc}");
若存在偏差,联系Azure支持调整实例时钟同步配置。
5. 启用Azure部署槽预热
利用部署槽的预热机制,让新实例完成Quartz初始化后再切换流量,避免新旧实例同时调度作业。
内容的提问来源于stack exchange,提问作者Dev
相关产品推荐
相关产品推荐

