Quartz.net Scheduler.Shutdown(true) 没有杀死工作
Quartz.net Scheduler.Shutdown(true) not killing jobs
我有一个 运行 石英工作并终止了我的 BackgroundService
,出于某种原因尽管调用 scheduler.Shutdown(true)
工作仍然 运行。
即使在循环和中断作业时,程序也会在线程退出之前关闭。
除了我下面的代码,我是否会考虑编写自定义 IScheduler 以确保 运行s 作业在关机时停止?
这是我的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);
}
如果勾选source code of generic host, you'll find that on host shutdown it waits for a default shutdown timeout, which is 5 seconds。这意味着如果您的作业需要更多时间才能完成,主机将超时退出,应用程序也将退出。
此外,根据您的评论,调度程序必须配置为在关机时中断 运行 个作业:
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)}().");
}
}
我有一个 运行 石英工作并终止了我的 BackgroundService
,出于某种原因尽管调用 scheduler.Shutdown(true)
工作仍然 运行。
即使在循环和中断作业时,程序也会在线程退出之前关闭。
除了我下面的代码,我是否会考虑编写自定义 IScheduler 以确保 运行s 作业在关机时停止?
这是我的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);
}
如果勾选source code of generic host, you'll find that on host shutdown it waits for a default shutdown timeout, which is 5 seconds。这意味着如果您的作业需要更多时间才能完成,主机将超时退出,应用程序也将退出。
此外,根据您的评论,调度程序必须配置为在关机时中断 运行 个作业:
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)}().");
}
}