如何在一天中的特定时间 运行 .net Core IHosted 服务?
How to run .net Core IHosted Service at specific time Of the day?
我在 .Net 核心 Ihosted 服务中有以下定时器 运行,
TimeSpan ScheduledTimespan;
string[] formats = { @"hh\:mm\:ss", "hh\:mm" };
string strTime = Startup.Configuration["AppSettings:JobStartTime"].ToString();
var success = TimeSpan.TryParseExact(strTime, formats, CultureInfo.InvariantCulture, out ScheduledTimespan);
Timer _timer = new Timer(JobToRun, null, TimeSpan.Zero, ScheduledTimespan);
我正在使用这个特定的重载,
public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period);
但是 JobToRun
在控件到达它时立即执行。
如何在每天的特定时间达到 运行?
提前致谢。
以下函数returns解析作业运行时间
private static TimeSpan getScheduledParsedTime()
{
string[] formats = { @"hh\:mm\:ss", "hh\:mm" };
string jobStartTime = "07:10";
TimeSpan.TryParseExact(jobStartTime, formats, CultureInfo.InvariantCulture, out TimeSpan ScheduledTimespan);
return ScheduledTimespan;
}
跟随函数returns 从当天当前时间开始的延迟时间。
如果一天中的当前时间超过作业 运行 时间,适当的延迟将被添加到作业 运行 中,如下所示,
private static TimeSpan getJobRunDelay()
{
TimeSpan scheduledParsedTime = getScheduledParsedTime();
TimeSpan curentTimeOftheDay = TimeSpan.Parse(DateTime.Now.TimeOfDay.ToString("hh\:mm"));
TimeSpan delayTime = scheduledParsedTime >= curentTimeOftheDay
? scheduledParsedTime - curentTimeOftheDay
: new TimeSpan(24, 0, 0) - curentTimeOftheDay + scheduledParsedTime;
return delayTime;
}
下面的计时器会根据JobRunDelay
每天
调用methodToExecute
_timer = new Timer(methodToExecute, null, getJobRunDelay(), new TimeSpan(24, 0, 0));
使用 Cron 作业
创建一个 ASP.NET 核心 Web 项目
添加 NuGet 包Microsoft.Extensions.Hosting
添加 NuGet 包 Cronos
Crete Abstract class CronJobService - 在这个 CronJobService 中,我们使用一个计时器来跟踪时间并在时间达到计划时触发后台任务
public abstract class CronJobService : IHostedService, IDisposable
{
private System.Timers.Timer _timer;
private readonly CronExpression _expression;
private readonly TimeZoneInfo _timeZoneInfo;
protected CronJobService(string cronExpression, TimeZoneInfo timeZoneInfo)
{
_expression = CronExpression.Parse(cronExpression);
_timeZoneInfo = timeZoneInfo;
}
public virtual async Task StartAsync(CancellationToken cancellationToken)
{
await ScheduleJob(cancellationToken);
}
protected virtual async Task ScheduleJob(CancellationToken cancellationToken)
{
var next = _expression.GetNextOccurrence(DateTimeOffset.Now, _timeZoneInfo);
if (next.HasValue)
{
var delay = next.Value - DateTimeOffset.Now;
if (delay.TotalMilliseconds <= 0) // prevent non-positive values from being passed into Timer
{
await ScheduleJob(cancellationToken);
}
_timer = new System.Timers.Timer(delay.TotalMilliseconds);
_timer.Elapsed += async (sender, args) =>
{
_timer.Dispose(); // reset and dispose timer
_timer = null;
if (!cancellationToken.IsCancellationRequested)
{
await DoWork(cancellationToken);
}
if (!cancellationToken.IsCancellationRequested)
{
await ScheduleJob(cancellationToken); // reschedule next
}
};
_timer.Start();
}
await Task.CompletedTask;
}
public virtual async Task DoWork(CancellationToken cancellationToken)
{
await Task.Delay(5000, cancellationToken); // do the work
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Stop();
await Task.CompletedTask;
}
public virtual void Dispose()
{
_timer?.Dispose();
}
}
继承自 CronJobService
public class XXXCronJob:CronJobService
{
private readonly ILogger<CronJob> _logger;
public XXXCronJob(IScheduleConfig<CronJob > config, ILogger<CronJob> logger)
: base(config.CronExpression, config.TimeZoneInfo)
{
_logger = logger;
}
public override Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("CronJob starts.");
return base.StartAsync(cancellationToken);
}
public override Task DoWork(CancellationToken cancellationToken)
{
_logger.LogInformation($"{DateTime.Now:hh:mm:ss} CronJob is working.");
return Task.CompletedTask;
}
public override Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("CronJob is stopping.");
return base.StopAsync(cancellationToken);
}
}
添加新接口 IScheduleConfig
public interface IScheduleConfig<T>
{
string CronExpression { get; set; }
TimeZoneInfo TimeZoneInfo { get; set; }
}
实施 IScheduleConfig
public class ScheduleConfig<T> : IScheduleConfig<T>
{
public string CronExpression { get; set; }
public TimeZoneInfo TimeZoneInfo { get; set; }
}
添加扩展class ScheduledServiceExtensions
public static class ScheduledServiceExtensions
{
public static IServiceCollection AddCronJob<T>(this IServiceCollection services, Action<IScheduleConfig<T>> options) where T : CronJobService
{
if (options == null)
{
throw new ArgumentNullException(nameof(options), @"Please provide Schedule Configurations.");
}
var config = new ScheduleConfig<T>();
options.Invoke(config);
if (string.IsNullOrWhiteSpace(config.CronExpression))
{
throw new ArgumentNullException(nameof(ScheduleConfig<T>.CronExpression), @"Empty Cron Expression is not allowed.");
}
services.AddSingleton<IScheduleConfig<T>>(config);
services.AddHostedService<T>();
return services;
}
}
使用泛型安排多个 Cron 作业
public void ConfigureServices(IServiceCollection services)
{
services.AddCronJob<XXXCronJob>(c =>
{
c.TimeZoneInfo = TimeZoneInfo.Local;
c.CronExpression = @"00 05 * * *";
});
services.AddCronJob<YYYCronJob>(c =>
{
c.TimeZoneInfo = TimeZoneInfo.Local;
c.CronExpression = @"00 08 * * *";
});
services.AddCronJob<ZZZCronJob>(c =>
{
c.TimeZoneInfo = TimeZoneInfo.Local;
c.CronExpression = @"30 14 * * *";
});
}
第一个是 05:00 上午。
第二个是 08:00 AM
最后一个是 14:30 下午。
我在 .Net 核心 Ihosted 服务中有以下定时器 运行,
TimeSpan ScheduledTimespan;
string[] formats = { @"hh\:mm\:ss", "hh\:mm" };
string strTime = Startup.Configuration["AppSettings:JobStartTime"].ToString();
var success = TimeSpan.TryParseExact(strTime, formats, CultureInfo.InvariantCulture, out ScheduledTimespan);
Timer _timer = new Timer(JobToRun, null, TimeSpan.Zero, ScheduledTimespan);
我正在使用这个特定的重载,
public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period);
但是 JobToRun
在控件到达它时立即执行。
如何在每天的特定时间达到 运行?
提前致谢。
以下函数returns解析作业运行时间
private static TimeSpan getScheduledParsedTime()
{
string[] formats = { @"hh\:mm\:ss", "hh\:mm" };
string jobStartTime = "07:10";
TimeSpan.TryParseExact(jobStartTime, formats, CultureInfo.InvariantCulture, out TimeSpan ScheduledTimespan);
return ScheduledTimespan;
}
跟随函数returns 从当天当前时间开始的延迟时间。 如果一天中的当前时间超过作业 运行 时间,适当的延迟将被添加到作业 运行 中,如下所示,
private static TimeSpan getJobRunDelay()
{
TimeSpan scheduledParsedTime = getScheduledParsedTime();
TimeSpan curentTimeOftheDay = TimeSpan.Parse(DateTime.Now.TimeOfDay.ToString("hh\:mm"));
TimeSpan delayTime = scheduledParsedTime >= curentTimeOftheDay
? scheduledParsedTime - curentTimeOftheDay
: new TimeSpan(24, 0, 0) - curentTimeOftheDay + scheduledParsedTime;
return delayTime;
}
下面的计时器会根据JobRunDelay
每天
methodToExecute
_timer = new Timer(methodToExecute, null, getJobRunDelay(), new TimeSpan(24, 0, 0));
使用 Cron 作业
创建一个 ASP.NET 核心 Web 项目
添加 NuGet 包Microsoft.Extensions.Hosting
添加 NuGet 包 Cronos
Crete Abstract class CronJobService - 在这个 CronJobService 中,我们使用一个计时器来跟踪时间并在时间达到计划时触发后台任务
public abstract class CronJobService : IHostedService, IDisposable { private System.Timers.Timer _timer; private readonly CronExpression _expression; private readonly TimeZoneInfo _timeZoneInfo; protected CronJobService(string cronExpression, TimeZoneInfo timeZoneInfo) { _expression = CronExpression.Parse(cronExpression); _timeZoneInfo = timeZoneInfo; } public virtual async Task StartAsync(CancellationToken cancellationToken) { await ScheduleJob(cancellationToken); } protected virtual async Task ScheduleJob(CancellationToken cancellationToken) { var next = _expression.GetNextOccurrence(DateTimeOffset.Now, _timeZoneInfo); if (next.HasValue) { var delay = next.Value - DateTimeOffset.Now; if (delay.TotalMilliseconds <= 0) // prevent non-positive values from being passed into Timer { await ScheduleJob(cancellationToken); } _timer = new System.Timers.Timer(delay.TotalMilliseconds); _timer.Elapsed += async (sender, args) => { _timer.Dispose(); // reset and dispose timer _timer = null; if (!cancellationToken.IsCancellationRequested) { await DoWork(cancellationToken); } if (!cancellationToken.IsCancellationRequested) { await ScheduleJob(cancellationToken); // reschedule next } }; _timer.Start(); } await Task.CompletedTask; } public virtual async Task DoWork(CancellationToken cancellationToken) { await Task.Delay(5000, cancellationToken); // do the work } public virtual async Task StopAsync(CancellationToken cancellationToken) { _timer?.Stop(); await Task.CompletedTask; } public virtual void Dispose() { _timer?.Dispose(); } }
继承自 CronJobService
public class XXXCronJob:CronJobService { private readonly ILogger<CronJob> _logger; public XXXCronJob(IScheduleConfig<CronJob > config, ILogger<CronJob> logger) : base(config.CronExpression, config.TimeZoneInfo) { _logger = logger; } public override Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("CronJob starts."); return base.StartAsync(cancellationToken); } public override Task DoWork(CancellationToken cancellationToken) { _logger.LogInformation($"{DateTime.Now:hh:mm:ss} CronJob is working."); return Task.CompletedTask; } public override Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("CronJob is stopping."); return base.StopAsync(cancellationToken); } }
添加新接口 IScheduleConfig
public interface IScheduleConfig<T> { string CronExpression { get; set; } TimeZoneInfo TimeZoneInfo { get; set; } }
实施 IScheduleConfig
public class ScheduleConfig<T> : IScheduleConfig<T> { public string CronExpression { get; set; } public TimeZoneInfo TimeZoneInfo { get; set; } }
添加扩展class ScheduledServiceExtensions
public static class ScheduledServiceExtensions { public static IServiceCollection AddCronJob<T>(this IServiceCollection services, Action<IScheduleConfig<T>> options) where T : CronJobService { if (options == null) { throw new ArgumentNullException(nameof(options), @"Please provide Schedule Configurations."); } var config = new ScheduleConfig<T>(); options.Invoke(config); if (string.IsNullOrWhiteSpace(config.CronExpression)) { throw new ArgumentNullException(nameof(ScheduleConfig<T>.CronExpression), @"Empty Cron Expression is not allowed."); } services.AddSingleton<IScheduleConfig<T>>(config); services.AddHostedService<T>(); return services; } }
使用泛型安排多个 Cron 作业
public void ConfigureServices(IServiceCollection services) { services.AddCronJob<XXXCronJob>(c => { c.TimeZoneInfo = TimeZoneInfo.Local; c.CronExpression = @"00 05 * * *"; }); services.AddCronJob<YYYCronJob>(c => { c.TimeZoneInfo = TimeZoneInfo.Local; c.CronExpression = @"00 08 * * *"; }); services.AddCronJob<ZZZCronJob>(c => { c.TimeZoneInfo = TimeZoneInfo.Local; c.CronExpression = @"30 14 * * *"; }); }
第一个是 05:00 上午。
第二个是 08:00 AM
最后一个是 14:30 下午。