Hangfire 可以在不重新部署的情况下处理计划任务的更改吗

问题描述 投票:0回答:3

我一直在 Microsoft MVC 应用程序中使用 Hangfire。我已经用它来编译和安排“即发即弃”任务,但令我惊讶的是,我无法在程序运行时添加/删除作业。 Hangfire是否真的无法在运行时动态调度任务?是否有一个众所周知的框架,即使在应用程序已编译或部署之后,也允许人们安排任务,而无需每次要添加任务时都更改 C# 代码?

我也研究过Quartz.NET,似乎也有同样的问题。

编辑:

Windows 任务计划程序可以允许使用 GUI 来计划任务,UNIX 的 cron 可以通过编辑文件来添加或删除任务,但我正在寻找某种在 Windows 上运行的应用程序,允许用户添加或删除任务应用程序部署之后的任务。我不想每次要添加或删除任务时都重新编译应用程序。

c# model-view-controller hangfire
3个回答
4
投票

正如所问,这个问题似乎源于对“动态......在运行时”含义的误解。答案是“是的”,它可以更改任务而无需重新部署(但这似乎不是您真正想要的)。

Hangfire 会向您的应用程序添加仪表板 UI(如果您对其进行配置),但它本身并不是端到端任务管理应用程序。它的设计目的是让“您的应用程序”能够安排工作,并以与调用点非常脱节的方式完成该工作——甚至可能无法在同一台机器上完成。 它仅限于调用 .NET 代码,但

根据定义

这满足了您所声明的“在运行时动态调度任务”的要求。这可以响应应用程序中您喜欢的任何事件来完成。任务也可以被删除、更新和取消。

后期编辑

)你是对的:任何调度 UI 或任务文件格式的反序列化都必须你自己编写。如果您正在寻找一个为您提供 UI 和/或任务文件 OOTB 的工具,您可能需要升级到像 JAMS 这样的商业产品。 (免责声明:这甚至可能本身不具备您需要的功能——我没有对该产品的直接经验,但与我共事过的人都积极地提到过它)。


2
投票

例如,假设您的代码中有硬编码的任务 A 和任务 B,并且您希望安排它们使用不同的参数动态运行。您可以创建一个 API,该 API 将使用您选择的参数在指定时间运行所需的任务。

[HttpPost] public IHttpActionResult Post([FromBody]TaskDto task) { var job = ""; if(task.TaskName == "TaskA"){ job = BackgroundJob.Schedule(() => RunTaskA(task.p1,task.p2), task.StartTime); } if(task.TaskName == "TaskB"){ job = BackgroundJob.Schedule(() => RunTaskB(task.p1,task.p2), task.StartTime); } if(!string.IsNullOrWhiteSpace(task.ContinueWith) && !string.IsNullOrWhiteSpace(job)){ if(task.ContinueWith == "TaskB"){ BackgroundJob.ContinueWith(job, () => RunTaskB(task.p3,task.p4)); } if(task.ContinueWith == "TaskA"){ BackgroundJob.ContinueWith(job, () => RunTaskA(task.p3,task.p4)); } } return Ok(job) }

然后您可以使用 JSON POST 调用 API(使用 javascript 的示例)

// Sending JSON data to start scheduled task via POST // var xhr = new XMLHttpRequest(); var url = "https://www.example.com/api/scheduletask"; xhr.open("POST", url, true); xhr.setRequestHeader("Content-type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var json = JSON.parse(xhr.responseText); } }; var data = JSON.stringify({"TaskName": "TaskA", "ContinueWith": "TaskB", "StartTime": "2-26-2018 10:00 PM", "p1": "myParam1", "p2": true, "p3": "myParam3", "p4": false}); xhr.send(data);

为了示例的完整性,这里是本示例的 TaskDto 类

public class TaskDto { public string TaskName { get; set; } public string ContinueWith { get; set; } public DateTime StartTime { get; set; } public string p1 { get; set; } public bool p2 { get; set; } public string p3 { get; set; } public bool p4 { get; set; } }



0
投票
这个要点

找到。 这是代码,以防要点消失:

ActualTimerJob.cs

using System; namespace CustomGist.Hangfire { public class ActualTimerJob : TimerJob<ActualTimerJob> { public override JobId => "Actual timer job"; protected override void PerformJobTasks(){ //do the actual job here with full access to _cancellationToken and _context } } }

ITimerJob.cs

using Hangfire; using Hangfire.Server; using System; namespace CustomGist.Hangfire { public interface ITimerJob { string JobId { get; } void Execute(IJobCancellationToken cancellationToken, PerformContext context); void Schedule(string cronExpression); } }

调度程序.cs

using Hangfire; using Hangfire.Storage; using System; namespace CustomGist.Hangfire { public class Scheduler { public static void ScheduleRecurringTasks(){ //Type the class name and CRON expression in, but could read this from json or xml if we wanted to //The class name is fully qualified and could be in another assembly. string className = "CustomGist.Hangfire.ActualTimerJob"; string cronExpression = Cron.Daily; try { var timerJobType = Type.GetType(className); var timerJobInstance = (ITimerJob) Activator.CreateInstance(timerJobType ?? throw new InvalidOperationException($"Unable to get type {className}")); timerJobInstance.Schedule(cronExpression); } catch (Exception e) { //Handle exception here, if something fails when dynamically loading the type. } } } }

TimerJob.cs

using Hangfire; using Hangfire.Server; using System; namespace CustomGist.Hangfire { public abstract class TimerJob<T> : ITimerJob where T : ITimerJob { public abstract string JobId { get; } protected IJobCancellationToken _cancellationToken; protected PerformContext _context; protected abstract void PerformJobTasks(); [DisableConcurrentExecution(0)] public void Execute(IJobCancellationToken cancellationToken, PerformContext context) { _cancellationToken = cancellationToken; _context = context; PerformJobTasks(); } public void Schedule(string cronExpression) { if (string.IsNullOrWhiteSpace(cronExpression)) { RecurringJob.RemoveIfExists(JobId); } RecurringJob.AddOrUpdate<T>(JobId, x => x.Execute(JobCancellationToken.Null, null), cronExpression, TimeZoneInfo.Local); } } }

© www.soinside.com 2019 - 2024. All rights reserved.