我有一个正在运行的石英作业并BackgroundService由于某种原因终止了我的scheduler.Shutdown(true).
即使在循环和中断作业时,程序也会在线程退出之前关闭。
除了我下面的代码,我是否会考虑编写自定义 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.");
}
Run Code Online (Sandbox Code Playgroud)
这是我的关机循环(直接调用关机不会中断任何正在运行的作业):
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我可以确认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)}().");
}
}
Run Code Online (Sandbox Code Playgroud)
我还从我在网上找到的内容中添加了以下内容,但它仍然等待关机:
var props = new NameValueCollection
{
{"quartz.scheduler.interruptJobsOnShutdownWithWait", "true"},
};
var scheduler = AsyncContext.Run(async () => await new StdSchedulerFactory(props).GetScheduler());
Run Code Online (Sandbox Code Playgroud)
我的解决方法是延迟允许作业在以下工作中终止,但是太狡猾了 - 请告知我如何在没有脆弱的任意延迟的情况下正常工作:
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);
}
Run Code Online (Sandbox Code Playgroud)
如果您检查通用主机的源代码,您会发现在主机关闭时它会等待默认的关闭超时,即5 秒。这意味着如果您的作业需要更多时间才能完成,主机将因超时退出,应用程序也将退出。
此外,根据您的评论,必须将调度程序配置为在关闭时中断正在运行的作业:
var props = new NameValueCollection
{
{"quartz.scheduler.interruptJobsOnShutdownWithWait", "true"},
};
var scheduler = AsyncContext.Run(async () => await new StdSchedulerFactory(props).GetScheduler());
Run Code Online (Sandbox Code Playgroud)
并使用设置为关闭的waitForJobsToComplete参数调用true:
await this.scheduler.Shutdown(waitForJobsToComplete: true, cancellationToken);
Run Code Online (Sandbox Code Playgroud)
确保调度程序仅在所有作业完成时退出。
为了保证应用程序仅在所有作业中断并完成后退出,您可以在主机退出后启动关闭:
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)}().");
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
152 次 |
| 最近记录: |