Quartz.net调度程序.关闭(true)不会杀死作业



我有一个正在运行的石英作业,并终止了我的BackgroundService,由于某种原因,尽管调用了scheduler.Shutdown(true),但该作业仍在运行。

即使在循环执行和中断作业时,程序也会在线程退出之前关闭if。

除了下面的代码之外,我会考虑写一个自定义的IScheduler来确保在关闭时停止运行作业吗?

这是我的IJob执行方法:

public async Task Execute(IJobExecutionContext context)
{
var cancellationToken = context.CancellationToken;
while (cancellationToken.IsCancellationRequested == false)
{
// Extension method so we catch TaskCancelled exceptions.
await TaskDelay.Wait(1000, cancellationToken);
Console.WriteLine("keep rollin, rollin, rollin...");
}
Console.WriteLine("Cleaning up.");
await Task.Delay(1000);
Console.WriteLine("Really going now.");
}

这是我的关机循环(直接调用关机不会中断任何正在运行的作业(:

internal class QuartzHostedService : IHostedService
{
// These are set by snipped constructor.
private readonly IJobSettings jobSettings;
private readonly ILogger logger;
private readonly IScheduler scheduler;
private async Task AddJobsToScheduler(CancellationToken cancellationToken = default)
{
var schedule = SchedulerBuilder.Create();
var downloadJob = JobBuilder.Create<StreamTickersJob>().Build();
var downloadJobTrigger = TriggerBuilder
.Create()
.ForJob(downloadJob)
.WithDailyTimeIntervalSchedule(
x => x.InTimeZone(serviceTimeZone)
.OnEveryDay()
.StartingDailyAt(new TimeOfDay(8,0))
.EndingDailyAt(new TimeOfDay(9,0)))
.Build();
await this.scheduler.ScheduleJob(downloadJob, downloadJobTrigger, cancellationToken);
}
public QuartzHostedService(IJobSettings jobSettings, IScheduler scheduler, ILogger<QuartzHostedService> logger)
{
this.jobSettings = jobSettings;
this.scheduler = scheduler;
this.logger = logger;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
this.logger.LogInformation("Quartz started...");
await AddJobsToScheduler(cancellationToken);
await this.scheduler.Start(cancellationToken);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await this.scheduler.PauseAll(cancellationToken);
foreach (var job in await this.scheduler.GetCurrentlyExecutingJobs(cancellationToken))
{
this.logger.LogInformation($"Interrupting job {job.JobDetail}");
await this.scheduler.Interrupt(job.JobDetail.Key, cancellationToken);
}
await this.scheduler.Shutdown(cancellationToken);
}
}

我可以确认IHost并没有突然杀死我的应用程序(至少没有几秒钟的测试暂停(,因为我在主程序的末尾设置了一个断点,如下所示:

public static void Main(string[] args)
{
// Wrap IHost in using statement to ensure disposal within scope.
using (var host = CreateHostBuilder(args)
.UseSerilog<Settings>(Settings.Name)
.UseConsoleLifetime()
.Build()
.UseSimpleInjector(container))
{
// Makes no difference if I shutdown jobs here.
// var lifetime = container.GetInstance<IHostApplicationLifetime>();            
// lifetime.ApplicationStarted.Register(async () => { });
// lifetime.ApplicationStopping.Register(async () => { });
var logger = container.GetInstance<ILogger<Program>>();
try
{
host.Run();
}
catch (Exception ex)
{
logger.LogCritical(ex, ex.Message);
}
// We reach here, whilst Jobs are still running :(
logger.LogDebug($"Finish {nameof(Main)}().");
}
}

我还添加了我在网上发现的以下内容,但它仍然不会等待关闭:

var props = new NameValueCollection
{
{"quartz.scheduler.interruptJobsOnShutdownWithWait", "true"},
};
var scheduler = AsyncContext.Run(async () => await new StdSchedulerFactory(props).GetScheduler());

我的变通方法是延迟允许以下作业终止,但很不可靠-请建议我如何在没有脆弱的任意延迟的情况下正常工作:

public async Task StopAsync(CancellationToken cancellationToken)
{
await this.scheduler.PauseAll(cancellationToken);
foreach (var job in await this.scheduler.GetCurrentlyExecutingJobs(cancellationToken))
{
this.logger.LogInformation($"Interrupting job {job.JobDetail}");
await this.scheduler.Interrupt(job.JobDetail.Key, cancellationToken);
}
await Task.Delay(3000);
await this.scheduler.Shutdown(cancellationToken);
}

如果您检查通用主机的源代码,您会发现在主机关闭时,它会等待默认的关闭超时,即5秒。这意味着,如果您的作业需要更多的时间才能完成,主机将超时退出,应用程序也将超时退出。

此外,根据您的评论,必须将调度器配置为在关机时中断正在运行的作业:

var props = new NameValueCollection
{
{"quartz.scheduler.interruptJobsOnShutdownWithWait", "true"},
};
var scheduler = AsyncContext.Run(async () => await new StdSchedulerFactory(props).GetScheduler());

并在waitForJobsToComplete参数设置为true的情况下调用以关闭:

await this.scheduler.Shutdown(waitForJobsToComplete: true, cancellationToken);

以确保调度器仅在所有作业完成时退出。

为了保证应用程序仅在所有作业中断和完成后退出,您可以在主机退出后启动关闭:

public static Task Main(string[] args)
{
using (var host = CreateHostBuilder(args)
.UseSerilog<Settings>(Settings.Name)
.UseConsoleLifetime()
.Build()
.UseSimpleInjector(container))
{
var logger = container.GetInstance<ILogger<Program>>();
try
{
await host.RunAsync();
var scheduller = container.GetInstance<IScheduler<Program>>();
scheduller.Shutdown(true);
}
catch (Exception ex)
{
logger.LogCritical(ex, ex.Message);
}
logger.LogDebug($"Finish {nameof(Main)}().");
}
}

相关内容

最新更新