在 ASP.NET Core 3.1 中,如何在未来的特定日期和时间安排带有托管服务的后台任务(Cron Jobs)?
In ASP.NET Core 3.1, how can I schedule a background task (Cron Jobs) with hosted services for a specific date and time in the future?
我正在开发一个基于 ASP.NET Core 3.1 的项目,我想为其添加一个特定的功能,以安排在 post 指定的日期和时间在未来发布 post =27=] 作者(类似于 Wordpress 通过其 cron 作业为计划的 posts 所做的事情)。例如,如果我们从用户那里收到这个日期和时间:
2020-09-07 14:08:07
那么,如何通过使用托管服务为它安排一次后台任务 运行 一次,然后更改数据库中的标志并在之后保存更改?
我读过一些关于它的文章,但他们没有指定日期和时间,只是提到每 5 秒重复执行一次任务以及诸如 cron 表达式之类的东西,但是,我需要知道的是如何才能我为特定日期和时间安排后台任务?
提前致谢。
使用DNTScheduler并设置具体的日期和时间
services.AddDNTScheduler(options =>
{
// DNTScheduler needs a ping service to keep it alive. Set it to false if you don't need it. Its default value is true.
// options.AddPingTask = false;
options.AddScheduledTask<DoBackupTask>(
runAt: utcNow =>
{
var now = utcNow.AddHours(3.5);
return now.Hour == 14 && now.Minute == 08 && now.Second == 07;
},
order: 1);
});
I want to add a specific functionality to it to schedule publishing a post in the future in a date and time specified by post author.For example, if we receive this date and time from user : 2020-09-07 14:08:07 .
Then, how can I schedule a background task for it by using hosted services to run only for one time and to change a flag in database and save changes after that?
看来你想在用户指定的日期时间执行后台task/job,为了达到要求,你可以尝试使用一些消息队列服务,例如Azure Queue Storage,这使我们能够通过设置 visibilityTimeout
.
来指定消息对 Dequeue 和 Peek 操作不可见的时间长度
当您的应用程序用户想要创建新的 post 并指定发布日期时间时,您可以将新消息(根据用户预期的日期时间指定 visibilityTimeout)插入队列中,以便这个新消息插入的消息将仅在队列中的指定日期时间可见。
QueueClient theQueue = new QueueClient(connectionString, "mystoragequeue");
if (null != await theQueue.CreateIfNotExistsAsync())
{
//The queue was created...
}
var newPost = "Post Content Here";
var user_specified_datetime = new DateTime(2020, 9, 9, 20, 50, 25);
var datetime_now = DateTime.Now;
TimeSpan duration = user_specified_datetime.Subtract(datetime_now);
await theQueue.SendMessageAsync(newPost, duration, default);
然后您可以执行队列触发的后台任务以从队列中检索消息并更新您的数据库记录。
注意:Microsoft Azure Storage Emulator是一个模拟Azure Queue等服务的工具,用于本地开发和测试目的,您可以尝试在本地针对存储服务测试代码无需创建 Azure 订阅或产生任何费用。
经过反复试验,我找到了一种方法,通过使用托管服务 为特定日期和时间安排 后台任务,正如我在问题中所问的那样,我做到了System.Threading.Timer
和 Timespan
像这样:
public class ScheduleTask : IScheduler, IDisposable
{
private Timer _timer;
private IBackgroundTaskQueue TaskQueue { get; }
// Set task to schedule for a specific date and time
public async Task SetAndQueueTaskAsync(ScheduleTypeEnum scheduleType, DateTime scheduleFor, Guid scheduledItemId)
{
// Omitted for simplicity
// ....
TaskQueue.QueueBackgroundWorkItem(SetTimer);
}
// ......
// lines omitted for simplicity
// .....
// Set timer for schedule item
private Task SetTimer(CancellationToken stoppingToken)
{
// ......
// lines omitted for simplicity
// .....
_timer = new Timer(DoWork, null, (item.ScheduledFor - DateTime.UtcNow).Duration(), TimeSpan.Zero);
return Task.CompletedTask;
}
private void DoWork(object state)
{
ScheduledItemChangeState(DateTime.UtcNow).Wait();
}
// Changes after the scheduled time comes
private async Task ScheduledItemChangeState(DateTime scheduleFor)
{
using (var scope = Services.CreateScope())
{
var context =
scope.ServiceProvider
.GetRequiredService<DataContext>();
// Changing some data in database
}
}
public void Dispose()
{
_timer?.Dispose();
}
}
如果你看上面代码中我传递 (item.ScheduledFor - DateTime.UtcNow)
作为 Timer class 构造函数的第三个参数来初始化它的新实例的部分,我实际上要求定时器做一个特定时间的特定工作我在 item.ScheduledFor
.
中存储为 DateTime
您可以在 ASP.NET Core 中阅读有关 托管服务后台任务的更多信息,请参阅官方 Microsoft 文档:
要在我的 Github 存储库中查看完整的实现,其中 可以在重新启动服务器后从数据库中恢复计划任务 ,请使用以下 link:
https://github.com/aspian-io/aspian/tree/master/Infrastructure/Schedule
我结合了 CrontabSchedule 和 IHostedService。下面的实现是轻量级的(没有强加库的体系结构)并且没有轮询。
public class SomeScheduledService: IHostedService
{
private readonly CrontabSchedule _crontabSchedule;
private DateTime _nextRun;
private const string Schedule = "0 0 1 * * *"; // run day at 1 am
private readonly SomeTask _task;
public SomeScheduledService(SomeTask task)
{
_task = Task;
_crontabSchedule = CrontabSchedule.Parse(Schedule, new CrontabSchedule.ParseOptions{IncludingSeconds = true});
_nextRun = _crontabSchedule.GetNextOccurrence(DateTime.Now);
}
public Task StartAsync(CancellationToken cancellationToken)
{
Task.Run(async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(UntilNextExecution(), cancellationToken); // wait until next time
await _task.Execute(); //execute some task
_nextRun = _crontabSchedule.GetNextOccurrence(DateTime.Now);
}
}, cancellationToken);
return Task.CompletedTask;
}
private int UntilNextExecution() => Math.Max(0, (int)_nextRun.Subtract(DateTime.Now).TotalMilliseconds);
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
我正在开发一个基于 ASP.NET Core 3.1 的项目,我想为其添加一个特定的功能,以安排在 post 指定的日期和时间在未来发布 post =27=] 作者(类似于 Wordpress 通过其 cron 作业为计划的 posts 所做的事情)。例如,如果我们从用户那里收到这个日期和时间:
2020-09-07 14:08:07
那么,如何通过使用托管服务为它安排一次后台任务 运行 一次,然后更改数据库中的标志并在之后保存更改?
我读过一些关于它的文章,但他们没有指定日期和时间,只是提到每 5 秒重复执行一次任务以及诸如 cron 表达式之类的东西,但是,我需要知道的是如何才能我为特定日期和时间安排后台任务?
提前致谢。
使用DNTScheduler并设置具体的日期和时间
services.AddDNTScheduler(options =>
{
// DNTScheduler needs a ping service to keep it alive. Set it to false if you don't need it. Its default value is true.
// options.AddPingTask = false;
options.AddScheduledTask<DoBackupTask>(
runAt: utcNow =>
{
var now = utcNow.AddHours(3.5);
return now.Hour == 14 && now.Minute == 08 && now.Second == 07;
},
order: 1);
});
I want to add a specific functionality to it to schedule publishing a post in the future in a date and time specified by post author.For example, if we receive this date and time from user : 2020-09-07 14:08:07 .
Then, how can I schedule a background task for it by using hosted services to run only for one time and to change a flag in database and save changes after that?
看来你想在用户指定的日期时间执行后台task/job,为了达到要求,你可以尝试使用一些消息队列服务,例如Azure Queue Storage,这使我们能够通过设置 visibilityTimeout
.
当您的应用程序用户想要创建新的 post 并指定发布日期时间时,您可以将新消息(根据用户预期的日期时间指定 visibilityTimeout)插入队列中,以便这个新消息插入的消息将仅在队列中的指定日期时间可见。
QueueClient theQueue = new QueueClient(connectionString, "mystoragequeue");
if (null != await theQueue.CreateIfNotExistsAsync())
{
//The queue was created...
}
var newPost = "Post Content Here";
var user_specified_datetime = new DateTime(2020, 9, 9, 20, 50, 25);
var datetime_now = DateTime.Now;
TimeSpan duration = user_specified_datetime.Subtract(datetime_now);
await theQueue.SendMessageAsync(newPost, duration, default);
然后您可以执行队列触发的后台任务以从队列中检索消息并更新您的数据库记录。
注意:Microsoft Azure Storage Emulator是一个模拟Azure Queue等服务的工具,用于本地开发和测试目的,您可以尝试在本地针对存储服务测试代码无需创建 Azure 订阅或产生任何费用。
经过反复试验,我找到了一种方法,通过使用托管服务 为特定日期和时间安排 后台任务,正如我在问题中所问的那样,我做到了System.Threading.Timer
和 Timespan
像这样:
public class ScheduleTask : IScheduler, IDisposable
{
private Timer _timer;
private IBackgroundTaskQueue TaskQueue { get; }
// Set task to schedule for a specific date and time
public async Task SetAndQueueTaskAsync(ScheduleTypeEnum scheduleType, DateTime scheduleFor, Guid scheduledItemId)
{
// Omitted for simplicity
// ....
TaskQueue.QueueBackgroundWorkItem(SetTimer);
}
// ......
// lines omitted for simplicity
// .....
// Set timer for schedule item
private Task SetTimer(CancellationToken stoppingToken)
{
// ......
// lines omitted for simplicity
// .....
_timer = new Timer(DoWork, null, (item.ScheduledFor - DateTime.UtcNow).Duration(), TimeSpan.Zero);
return Task.CompletedTask;
}
private void DoWork(object state)
{
ScheduledItemChangeState(DateTime.UtcNow).Wait();
}
// Changes after the scheduled time comes
private async Task ScheduledItemChangeState(DateTime scheduleFor)
{
using (var scope = Services.CreateScope())
{
var context =
scope.ServiceProvider
.GetRequiredService<DataContext>();
// Changing some data in database
}
}
public void Dispose()
{
_timer?.Dispose();
}
}
如果你看上面代码中我传递 (item.ScheduledFor - DateTime.UtcNow)
作为 Timer class 构造函数的第三个参数来初始化它的新实例的部分,我实际上要求定时器做一个特定时间的特定工作我在 item.ScheduledFor
.
您可以在 ASP.NET Core 中阅读有关 托管服务后台任务的更多信息,请参阅官方 Microsoft 文档:
要在我的 Github 存储库中查看完整的实现,其中 可以在重新启动服务器后从数据库中恢复计划任务 ,请使用以下 link:
https://github.com/aspian-io/aspian/tree/master/Infrastructure/Schedule
我结合了 CrontabSchedule 和 IHostedService。下面的实现是轻量级的(没有强加库的体系结构)并且没有轮询。
public class SomeScheduledService: IHostedService
{
private readonly CrontabSchedule _crontabSchedule;
private DateTime _nextRun;
private const string Schedule = "0 0 1 * * *"; // run day at 1 am
private readonly SomeTask _task;
public SomeScheduledService(SomeTask task)
{
_task = Task;
_crontabSchedule = CrontabSchedule.Parse(Schedule, new CrontabSchedule.ParseOptions{IncludingSeconds = true});
_nextRun = _crontabSchedule.GetNextOccurrence(DateTime.Now);
}
public Task StartAsync(CancellationToken cancellationToken)
{
Task.Run(async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(UntilNextExecution(), cancellationToken); // wait until next time
await _task.Execute(); //execute some task
_nextRun = _crontabSchedule.GetNextOccurrence(DateTime.Now);
}
}, cancellationToken);
return Task.CompletedTask;
}
private int UntilNextExecution() => Math.Max(0, (int)_nextRun.Subtract(DateTime.Now).TotalMilliseconds);
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}