Quartz.NET作为一个开源的作业调度库,广泛应用于.NET应用程序中,以实现复杂的定时任务,本次记录利用Quartz.NET实现HTTP作业调度,通过自定义HTTP作业,实现对外部API的定时调用和如何管理这些作业,包括创建、修改、暂停、恢复和删除作业。

1.首先定义了一个HttpJob类,该类实现了IJob接口,用于执行HTTP请求。利用了RestRequest来构建请求,并通过静态字典Delegates存储每个作业的配置信息,如URL、请求方法和请求头等

public class HttpJob : IJob
{
public static readonly Dictionary<string, HttpJobInfo> Delegates = new(); public async Task Execute(IJobExecutionContext context)
{
var delegateKey = context.JobDetail.JobDataMap.GetString("delegateKey");
if (delegateKey != null && Delegates.TryGetValue(delegateKey, out var func))
{
var requestBody = new RestRequest();
if (func.Headers != null)
{
foreach (var header in func.Headers)
{
requestBody.AddHeader(header.Key, header.Value);
}
} var content = HttpHelper.HttpRequest(func.Url, func.Request, requestBody);
JobLogHelper.AddJobLog(new JobLog() { JobName = context.JobDetail.Key.Name, GroupName = context.JobDetail.Key.Group, RunTime = DateTime.Now, RunResult = content });
UpdateLastExecutionTime(context.JobDetail.Key.Name, context.JobDetail.Key.Group, DateTime.Now);
}
await Task.CompletedTask;
}
}

2.作业信息的持久化:为了持久化作业信息,定义了JobInfo类来存储作业的基本信息,如名称、组名、Cron表达式等,并将这些信息保存在本地的JSON文件中。

public class JobInfo
{
public required string JobName { get; set; }
public required string GroupName { get; set; }
public required string CronExpression { get; set; }
public DateTime LastExecutionTime { get; set; }
public JobStatus Status { get; set; }
public required HttpJobInfo HttpJob { get; set; }
}

3.实现了QuartzHelper类,用于管理作业的生命周期。这包括加载作业信息、创建作业、调度作业、暂停/恢复作业以及删除作业等功能。

 public class QuartzHelper
{
private IScheduler scheduler;
private List<JobInfo> jobInfos; private string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "jobs.json"); /// <summary>
/// 构造函数,初始化定时任务管理器
/// </summary>
public QuartzHelper()
{
ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
scheduler = schedulerFactory.GetScheduler().Result;
scheduler.Start().Wait();
LoadJobInfosApi().Wait(); }
/// <summary>
/// 保存作业信息到本地 JSON 文件
/// </summary>
private void SaveJobInfos()
{
string json = JsonConvert.SerializeObject(jobInfos);
File.WriteAllText(filePath, json);
} /// <summary>
/// 加载本地 JSON 文件中的作业信息
/// </summary>
private async Task LoadJobInfosApi()
{
if (File.Exists(filePath))
{
string json = File.ReadAllText(filePath);
jobInfos = JsonConvert.DeserializeObject<List<JobInfo>>(json) ?? new List<JobInfo>();
foreach (var jobInfo in jobInfos)
{ // 创建委托的唯一键
var delegateKey = Guid.NewGuid().ToString();
// 将委托存储在静态字典中
HttpJob.Delegates[delegateKey] = jobInfo.HttpJob; // 创建并调度作业
IJobDetail job = JobBuilder.Create<HttpJob>()
.WithIdentity(jobInfo.JobName, jobInfo.GroupName).UsingJobData("delegateKey", delegateKey) // 将委托的键添加到JobDataMap
.Build(); ITrigger trigger = TriggerBuilder.Create()
.WithIdentity(jobInfo.JobName, jobInfo.GroupName)
.WithCronSchedule(jobInfo.CronExpression)
//.StartNow()
.Build(); await scheduler.ScheduleJob(job, trigger); // 根据任务状态恢复或暂停任务
if (jobInfo.Status == JobStatus.正常运行)
{
await ResumeJob(jobInfo.JobName, jobInfo.GroupName);
}
else
{
await PauseJob(jobInfo.JobName, jobInfo.GroupName);
}
}
}
else
{
jobInfos = new List<JobInfo>();
}
} #region 执行普通任务时使用,传委托时可以参考此方法
///// <summary>
///// 新建任务并立即执行
///// </summary>
//[Obsolete("执行普通任务时使用,可以传委托使用")]
//public async Task AddJob(string jobName, string groupName, string cronExpression, Func<bool> func, string description = "")
//{
// if (jobInfos.Any(c => c.JobName == jobName && c.GroupName == groupName))
// {
// return;
// } // // 创建委托的唯一键
// var delegateKey = Guid.NewGuid().ToString();
// // 将委托存储在静态字典中
// // MyJobClass.Delegates[delegateKey] = func; // // 创建作业信息并保存到列表 需要将func 加入到jobInfo 中做作业持久化!!!!
// var jobInfo = new JobInfo { JobName = jobName, GroupName = groupName, CronExpression = cronExpression, Status = JobStatus.正常运行, Description = description, JobCreateTime = DateTime.Now };
// jobInfos.Add(jobInfo);
// SaveJobInfos(); // // 创建Quartz作业和触发器
// IJobDetail job = JobBuilder.Create<MyJobClass>()
// .WithIdentity(jobName, groupName)
// .UsingJobData("delegateKey", delegateKey) // 将委托的键添加到JobDataMap
// .Build(); // ITrigger trigger = TriggerBuilder.Create()
// .WithIdentity(jobName + "Trigger", groupName)
// .StartNow()
// .WithCronSchedule(cronExpression).WithDescription(description)
// .Build(); // await scheduler.ScheduleJob(job, trigger); //} #endregion /// <summary>
/// 新建任务并立即执行
/// </summary> public async Task AddJobApi(string jobName, string groupName, string cronExpression, HttpJobInfo httpJobInfo, string description = "")
{
if (jobInfos.Any(c => c.JobName == jobName && c.GroupName == groupName))
{
return;
} // 创建委托的唯一键
var delegateKey = Guid.NewGuid().ToString();
// 将委托存储在静态字典中
HttpJob.Delegates[delegateKey] = httpJobInfo; // 创建作业信息并保存到列表 需要将func 加入到jobInfo 中做作业持久化!!!!
var jobInfo = new JobInfo { JobName = jobName, GroupName = groupName, CronExpression = cronExpression, HttpJob = httpJobInfo, Status = JobStatus.正常运行, Description = description, JobCreateTime = DateTime.Now };
jobInfos.Add(jobInfo);
SaveJobInfos(); // 创建Quartz作业和触发器
IJobDetail job = JobBuilder.Create<HttpJob>()
.WithIdentity(jobName, groupName)
.UsingJobData("delegateKey", delegateKey) // 将委托的键添加到JobDataMap
.Build(); ITrigger trigger = TriggerBuilder.Create()
.WithIdentity(jobName + "Trigger", groupName)
.StartNow()
.WithCronSchedule(cronExpression).WithDescription(description)
.Build(); await scheduler.ScheduleJob(job, trigger); } /// <summary>
/// 暂停任务
/// </summary>
public async Task PauseJob(string jobName, string groupName)
{
await scheduler.PauseJob(new JobKey(jobName, groupName));
var job = jobInfos.FirstOrDefault(j => j.JobName == jobName && j.GroupName == groupName);
if (job != null)
{
job.Status = JobStatus.暂停;
SaveJobInfos();
}
} /// <summary>
/// 开启任务
/// </summary>
public async Task ResumeJob(string jobName, string groupName)
{
await scheduler.ResumeJob(new JobKey(jobName, groupName));
var job = jobInfos.FirstOrDefault(j => j.JobName == jobName && j.GroupName == groupName);
if (job != null)
{
job.Status = JobStatus.正常运行;
SaveJobInfos();
}
} /// <summary>
/// 立即执行任务
/// </summary>
public async Task TriggerJob(string jobName, string groupName)
{
await scheduler.TriggerJob(new JobKey(jobName, groupName));
var job = jobInfos.FirstOrDefault(j => j.JobName == jobName && j.GroupName == groupName);
if (job != null)
{
job.LastExecutionTime = DateTime.Now;
SaveJobInfos();
}
} /// <summary>
/// 修改任务
/// </summary>
public async Task ModifyJob(string jobName, string groupName, string cronExpression, HttpJobInfo httpJobInfo, string description = "")
{
await DeleteJob(jobName, groupName);
await AddJobApi(jobName, groupName, cronExpression, httpJobInfo, description);
}
/// <summary>
/// 删除任务
/// </summary>
public async Task DeleteJob(string jobName, string groupName)
{
await scheduler.DeleteJob(new JobKey(jobName, groupName));
jobInfos.RemoveAll(j => j.JobName == jobName && j.GroupName == groupName);
SaveJobInfos();
} /// <summary>
/// 获取当前所有任务列表
/// </summary>
public List<JobInfo> GetAllJobs()
{
if (File.Exists(filePath))
{
string json = File.ReadAllText(filePath);
jobInfos = JsonConvert.DeserializeObject<List<JobInfo>>(json) ?? new List<JobInfo>();
return jobInfos;
}
else
return null; } }

QuartzHelper

4.为了跟踪作业的执行情况,设计了JobLog类和JobLogHelper类,用于记录和查询作业执行日志。

public class JobLogHelper
{
private static string _filePath; /// <summary>
/// 根据作业名称和组名称获取当日的作业执行日志
/// </summary>
/// <param name="jobName"></param>
/// <param name="groupName"></param>
/// <returns></returns>
public static List<JobLog> GetJobLog(string jobName, string groupName)
{
_filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"jobsLog-{DateTime.Now:yyyyMMdd}.json"); // 检查文件是否存在
if (!File.Exists(_filePath))
{
return new List<JobLog>();
}
var jsonText = $"[{File.ReadAllText(_filePath)}]";
var list = JsonConvert.DeserializeObject<List<JobLog>>(jsonText);
if (list != null)
{
var result = list.Where(c => c.JobName == jobName && groupName == c.GroupName).OrderByDescending(c => c.RunTime).ToList();
return result;
} return null;
}
/// <summary>
///获取所有的 作业执行日志 //可以从这里拓展其他查询条件
/// </summary>
/// <returns></returns>
public static List<JobLog> GetAllLogs()
{
List<JobLog> jobLogs = new List<JobLog>();
var logFilePaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "jobsLog-*.json");
logFilePaths.ToList().ForEach(c =>
{
var jsonText = $"[{File.ReadAllText(_filePath)}]";
var list = JsonConvert.DeserializeObject<List<JobLog>>(jsonText);
if (list != null) jobLogs.AddRange(list);
});
return jobLogs;
}
/// <summary>
/// 添加作业执行日志
/// </summary>
/// <param name="jobLog"></param>
public static void AddJobLog(JobLog jobLog)
{
_filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"jobsLog-{DateTime.Now:yyyyMMdd}.json");
string json = JsonConvert.SerializeObject(jobLog) + ",\n";
File.AppendAllText(_filePath, json);
}
}

作业执行日志

5.最后,通过ASP.NET Core的Controller提供了一系列Web API接口,以便于通过HTTP请求管理作业。这些接口包括获取作业列表、添加作业、修改作业、删除作业、暂停作业、恢复作业和立即执行作业等。

 [Route("api/[controller]")]
[ApiController]
public class QuartzController : ControllerBase
{
private readonly QuartzHelper _quartzHelper;
public QuartzController(QuartzHelper quartzHelper)
{
_quartzHelper = quartzHelper;
} [HttpGet]
[Route("job/GetJobs")]
public object GetJobs()
{
return Ok(new {code=200,data = _quartzHelper.GetAllJobs() });
} [HttpGet]
[Route("job/GetJobLog")]
public object GetJobLog(string jobName, string groupName)
{
return Ok(new { code = 200, data = JobLogHelper.GetJobLog(jobName, groupName) });
}
[HttpGet]
[Route("job/GetJobLogs")]
public object GetJobLogs()
{
return Ok(new { code = 200, data = JobLogHelper.GetAllLogs() });
} [HttpPost]
[Route("job/AddJob")]
public async Task<object> Add(JobInfo jobInfo)
{
try
{
await _quartzHelper.AddJobApi(jobInfo.JobName, jobInfo.GroupName, jobInfo.CronExpression, jobInfo.HttpJob, jobInfo.Description);
return Ok(new { code = 200, msg = "创建成功!" });
}
catch (Exception ex)
{
return Ok(new { code = 500, msg = ex.Message });
}
} [HttpPost]
[Route("job/ModifyJob")]
public async Task<object> Edit(JobInfo jobInfo)
{
try
{
await _quartzHelper.ModifyJob(jobInfo.JobName, jobInfo.GroupName, jobInfo.CronExpression, jobInfo.HttpJob, jobInfo.Description);
return Ok(new { code = 200, msg = "修改成功!" });
}
catch (Exception ex)
{
return Ok(new { code = 500, msg = ex.Message });
}
} [HttpGet]
[Route("job/DeleteJob")]
public async Task<object> Delete(string jobName, string groupName)
{
try
{
await _quartzHelper.DeleteJob(jobName, groupName);
return Ok(new { code = 200, msg = "删除成功!" });
}
catch (Exception ex)
{
return Ok(new { code = 500, msg = ex.Message });
}
} [HttpGet]
[Route("job/PauseJob")]
public async Task<object> PauseJob(string jobName, string groupName)
{
try
{
await _quartzHelper.PauseJob(jobName, groupName);
return Ok(new { code = 200, msg = "暂停成功!" });
}
catch (Exception ex)
{
return Ok(new { code = 500, msg = ex.Message });
}
} [HttpGet]
[Route("job/ResumeJob")]
public async Task<object> ResumeJob(string jobName, string groupName)
{
try
{
await _quartzHelper.ResumeJob(jobName, groupName);
return Ok(new { code = 200, msg = "开启任务成功!" });
}
catch (Exception ex)
{
return Ok(new { code = 500, msg = ex.Message });
}
}
[HttpGet]
[Route("job/TriggerJob")]
public async Task<object> TriggerJob(string jobName, string groupName)
{
try
{
await _quartzHelper.TriggerJob(jobName, groupName);
return Ok(new { code = 200, msg = "立即执行任务命令已执行!" });
}
catch (Exception ex)
{
return Ok(new { code = 500, msg = ex.Message });
}
}
}

Web API接口

源码地址:https://github.com/yycb1994/Quartz.Net

实现Quartz.NET的HTTP作业调度的更多相关文章

  1. Quartz.Net入门 - Net作业调度

    背景 很多时候,项目需要在不同时刻,执行一个或很多个不同的作业. Windows执行计划这时并不能很好的满足需求了,迫切需要一个更为强大,方便管理,集群部署的作业调度框架. 介绍 Quartz一个开源 ...

  2. Quartz.NET 2.0 作业调度框架使用

    Quartz.NET是一个开源的作业调度框架,是 OpenSymphony 的 Quartz API 的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不 ...

  3. Net作业调度(一) -Quartz.Net入门

    背景 很多时候,项目需要在不同时刻,执行一个或很多个不同的作业. Windows执行计划这时并不能很好的满足需求了,迫切需要一个更为强大,方便管理,集群部署的作业调度框架. 介绍 Quartz一个开源 ...

  4. Net作业调度-----Quartz.Net

    一:业务需求: 项目需要在不同时刻,执行一个或很多个不同的作业. Windows执行计划这时并不能很好的满足需求了,迫切需要一个更为强大,方便管理,集群部署的作业调度框架. 二:介绍 Quartz一个 ...

  5. Quartz.NET——作业调度组件

    之前有个旧同事说他在项目中碰到某些功能需要使用到作业调度,于是找到了这个组件,据说相当好用,叫我有时间的话去了解一下.哈,于是小了解了一下,基本的使用算是明白了,深层次的东西就不了解了,本文简单记录一 ...

  6. Net作业调度(一) -Quartz.Net入门 Quartz表达式生成器 [转]

    背景 很多时候,项目需要在不同个时刻,执行一个或很多个不同的作业. Windows执行计划这时并不能很好的满足需求了. 这时候需要一个更为强大,方便管理,集部署的作业调度了. 介绍 Quartz一个开 ...

  7. Quartz和TopShelf Windows服务作业调度

    上一次写了一遍关于Quartz作业调度的文章 Quartz.NET 作业调度使用 现在使用TopShelf和Quartz实现windows服务作业调度 TopShelf版本4.0 Quartz版本3. ...

  8. 作业调度框架Quartz.NET | 大专栏

    原文:作业调度框架Quartz.NET | 大专栏 作业调度框架Quartz.NET 发表于 2019-09-28 |  分类于 前端 |  没有评论 前言 任务调度系统并不是完美的,它会出现任务执行 ...

  9. topshelf和quartz内部分享

    阅读目录: 介绍 基础用法 调试及安装 可选配置 多实例支持及相关资料 quartz.net 上月在公司内部的一次分享,现把PPT及部分交流内容整理成博客. 介绍 topshelf是创建windows ...

  10. Quartz.Net实现定时任务调度

    Quartz.Net介绍: Quartz一个开源的作业调度框架,OpenSymphony的开源项目.Quartz.Net 是Quartz的C#移植版本. 它一些很好的特性: 1:支持集群,作业分组,作 ...

随机推荐

  1. Ubuntu开启root账户登陆

    Ubuntu开启root账户登陆 设置 root 密码:sudo passwd root sudo chmod 777 /usr/share/lightdm/lightdm.conf.d/50-xx ...

  2. python教程2:if...else...+循环

    一.if判断 有单分支.双分支.多分支,下面就是一个多分支的案例: 二.缩进 三.for循环 四.while循环  五.其他 random模块  string模块

  3. VNC远程控制软件是什么?有没有更好的远程桌面控制解决方案?

    看官老爷们,你们是否需要远程访问或远程支持解决方案?来了解下VNC吧. 什么是VNC? VNC是虚拟网络计算(VNC)是一种远程桌面共享技术,用于从世界任何地方远程访问和控制计算机. VNC的工作原理 ...

  4. AI回答总不满意?你的提问方式可能完全错误!

    AI回答总不满意?你的提问方式可能完全错误! 大家好,我是卷福同学,一个专注AI大模型整活的前阿里程序员,腾讯云社区2023新秀突破作者 向AI提问想写一篇论文,结果AI就生成2000字左右的文章后就 ...

  5. pwn杂项之linux命令执行

    通常pwn题目,时常会考到对Linux命令的一些使用,比如当cat被禁用的时候,可以使用tac,或者别的命令代替 下面是buu上的应该题目,考察的就是对liunx命令的理解,以及对程序的分析. 题目地 ...

  6. nginx学习记录【二】nginx跟.net core结合,实现一个域名访问多个.net core应用

    1.实现转发 打开conf下的nginx.conf文件,如下图: 2.添加.net core网站的转发 按下面的进行修改,修改完后,就把localhost的80转发到了https://localhos ...

  7. k8s中的pod更新策略

    StatefulSet(有状态集,缩写为sts)常用于部署有状态的且需要有序启动的应用程序,比如在进行SpringCloud项目容器化时,Eureka的部署是比较适合用StatefulSet部署方式的 ...

  8. nginx重载流程nginx请求处理流程nginx单进程和多进程

    nginx重载流程 首先nginx会向master进程发送HUP信号[reload命令] master进程校验配置语法是否正确 master进程打开新的监听端口 master进程用心配置启动新的wor ...

  9. BGP中next-hop-self 小实验

    next-hop-self 在EBGP和IBGP边界使用,对ibgp下一跳邻居使用 配置命令 router bgp 1234 neighbor 2.2.2.2 next-hop-self 使用Next ...

  10. (性能测试)--记录一次高可用场景导致CPU资源升高

    测试场景:高可用场景--限流测试: 被测交易:查询类交易,HTTP协议: 交易链路:jmeter - web - coimpre(前置服务) -- coimbp -- cobp (coimbp .co ...