如何让Hangfire即发即弃任务队列仅在指定时段运行?
实现Hangfire队列仅在指定时段执行任务的方案
完全可以实现让alpha队列的任务仅在每日指定时段(如00:00-03:00)执行,以下是几种实用的实现方式:
方案一:全局任务过滤器自动推迟非时段任务
通过Hangfire的任务过滤器,在任务准备执行前检查当前时间,若不在允许时段则自动推迟到下一个时段执行,无需修改任务提交逻辑。
步骤1:实现时间限制过滤器
public class TimeRestrictionFilter : IElectStateFilter { public void OnStateElection(ElectStateContext context) { // 仅处理待入队状态的任务 if (context.CandidateState.Name != EnqueuedState.StateName) return; var now = DateTime.Now; // 定义允许执行的时间范围(00:00-03:00) var allowedStart = new TimeSpan(0, 0, 0); var allowedEnd = new TimeSpan(3, 0, 0); var currentTime = now.TimeOfDay; if (!(currentTime >= allowedStart && currentTime <= allowedEnd)) { // 计算距离下一次允许时段的时间差 var nextAllowedStartTime = now.Date.AddDays(1).Add(allowedStart); var delay = nextAllowedStartTime - now; // 将任务改为调度状态,推迟到下一个时段执行 context.CandidateState = new ScheduledState(delay) { Reason = "当前不在允许执行时段,自动推迟到下一个时段" }; } } }
步骤2:注册全局过滤器
在Hangfire配置代码中添加这个过滤器:
GlobalJobFilters.Filters.Add(new TimeRestrictionFilter());
方案二:定时启停Alpha队列的Hangfire Server
通过定时任务控制处理alpha队列的Server仅在指定时段运行,非时段内Server完全停止,不会处理队列中的任务。
代码示例
// 启动处理默认队列的Server(如需保留其他队列处理) app.UseHangfireServer(); // 用于存储alpha队列Server的实例 private static BackgroundJobServer _alphaQueueServer; // 启动alpha队列Server的方法 public void StartAlphaQueueServer() { if (_alphaQueueServer == null) { var alphaOptions = new BackgroundJobServerOptions { Queues = new[] { "alpha" } }; _alphaQueueServer = new BackgroundJobServer(alphaOptions); } } // 停止alpha队列Server的方法 public void StopAlphaQueueServer() { _alphaQueueServer?.Dispose(); _alphaQueueServer = null; } // 在应用启动时添加定时启停任务 RecurringJob.AddOrUpdate(() => StartAlphaQueueServer(), "0 0 * * *"); // 每日00:00启动 RecurringJob.AddOrUpdate(() => StopAlphaQueueServer(), "0 3 * * *"); // 每日03:00停止
方案三:提交任务时判断时间,直接调度到指定时段
从任务提交的源头控制,若当前不在允许时段,则直接将任务调度到下一个时段,而非立即入队。
代码示例
public void QueueNotification(string email) { var now = DateTime.Now; var allowedStart = new TimeSpan(0, 0, 0); var allowedEnd = new TimeSpan(3, 0, 0); var currentTime = now.TimeOfDay; if (currentTime >= allowedStart && currentTime <= allowedEnd) { // 时段内直接入队执行 BackgroundJob.Enqueue<YourService>(x => x.SendNotification(email)); } else { // 计算下一个允许时段的开始时间 var nextRunTime = now.Date.AddDays(1).Add(allowedStart); // 调度到下一个时段执行 BackgroundJob.Schedule<YourService>(x => x.SendNotification(email), nextRunTime); } }
方案对比
- 方案一:无需修改现有任务提交代码,全局管控所有
alpha队列任务,适合已有多处任务提交逻辑的场景。 - 方案二:非时段彻底停止队列处理,节省服务器资源,适合任务量较大、对资源占用敏感的场景。
- 方案三:从源头控制任务执行时间,队列中不会堆积非时段任务,适合任务提交频率较低的场景。
内容的提问来源于stack exchange,提问作者Can
相关产品推荐
相关产品推荐

