.Net Core实现基于Quart.Net的任务管理
前段时间给公司项目升级.net框架,把原先的任务管理平台用.net core实现,现做如下整理:
一、实现思路
之前的实现也是参考了博客园中其他文章实现的思路:
- 一个任务定义一个实现IJob接口的类,通过单独的dll管理;
- 通过数据库持久化、维护任务,便于服务重启时任务的恢复;
- 定义一个管理任务的基础服务,轮询数据库中的任务,根据任务的状态维护任务的执行;
- 新增任务时,需要在数据库中添加一条记录,并且在任务管理的dll中添加一个实现IJob的类,基础服务通过反射dll来构建任务的实例添加到调度器中
由于业务代码会频繁调整,我们业务代码从任务执行中拆分出来,独立部署成http服务,任务的执行就是调用一个http请求,这样不同的任务就是请求的url不一样,查看官方文档( https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/more-about-jobs.html#job-instances )发现,我们可以通过只创建一个基础任务类,创建多个该任务类的实例来实现构建多个任务,IJobDetail中可以用JobDataMap对象来存储Job实例的参数,所以我们通过JobDataMap将请求url传递到任务的Execute()方法中,我们只需要在数据库中补充任务请求的url信息就可以了,不需要单独的dll去定义任务。
二、项目结构
根据上面思路,我们只需要一个管理任务的基础服务、一个Web管理平台就可以实现,为了保持项目简单,把任务管理无关的功能合并在一个项目里,并且尽量排除无关的框架和功能点,最终程序包含3个项目:
- JobManage.Service:控制台程序,管理任务的基础服务,通过Topshelf部署成windows服务,如何部署参考: https://www.cnblogs.com/podolski/p/10054286.html
- JobManage.Web:Web应用程序,管理平台,新增、暂停、恢复、删除任务,查看任务运行日志;
- JobManage.Core:类库,提供业务基础服务,如数据库操作等
动态添加任务:
IJobDetail jobDetail = JobBuilder.Create<BaseJob>()
.WithIdentity(jobKey)
.UsingJobData("RequestUrl", job.RequestUrl)
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity(group, name)
.StartNow()
.WithCronSchedule(job.CronExpression)
.Build();
await context.Scheduler.ScheduleJob(jobDetail, trigger);
基础任务类BaseJob.cs的Execute()方法:
public async Task Execute(IJobExecutionContext context)
{
var url = context.JobDetail.JobDataMap.GetString("RequestUrl");
var client = _clientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Post, url);
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
await response.Content.ReadAsStringAsync();
}
}
三、任务状态管理
这里定义7个任务状态:待执行、执行中、待暂停、已暂停、待恢复、待删除、已删除
web管理平台维护任务(新增、暂停、恢复、删除)时将任务状态更新为待处理状态(待执行、待暂停、待恢复、待删除),任务管理基础服务定时遍历业务任务,根据数据库中任务当前的状态修改任务的执行,并且将数据库中待处理任务状态更新为已处理状态(执行中、已暂停、已删除)
四、任务依赖注入服务
在任务类中我们用到了http服务,我们需要在任务类中获取http服务,我们通过.Net Core注入和获取服务的方式来实现,这里主要是要自定义任务类实例的创建和获取,官方文档( https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/miscellaneous-features.html#jobfactory )中说明可以通过实现 IJobFactory 接口,并且修改 IScheduler.JobFactory的属性来实现:
//自定义任务实例获取
public class JobFactory : IJobFactory
{
private readonly IServiceProvider _serviceProvider;
public JobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public virtual IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
IJobDetail jobDetail = bundle.JobDetail;
Type jobType = jobDetail.JobType;
return _serviceProvider.GetService(jobType) as IJob;
}
public virtual void ReturnJob(IJob job)
{
var disposable = job as IDisposable;
disposable?.Dispose();
}
}
//修改IScheduler.JobFactory属性
_scheduler.JobFactory = serviceProvider.GetService<JobFactory>();
官方文档中也提供了依赖注入的示例: https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/microsoft-di-integration.html#di-aware-job-factories
五、任务监听
我们需要记录任务执行的情况,Quartz.Net提供了任务监听功能,我们可以自己实现IJobListener接口,也可以继承Quartz.Net框架中IJobListener的实现类JobListenerSupport来完成任务的监听,继承JobListenerSupport 类时重写对应的方法来实现我们需要的操作,如下实现记录任务上次执行时间、下次执行时间、执行时长、执行异常错误信息
//监听实现
public class JobListener : JobListenerSupport
{
private readonly JobRepository _jobRepository;
private readonly JobRunLogRepository _jobRunLogRepository;
public JobListener(JobRepository jobRepository, JobRunLogRepository jobRunLogRepository)
{
_jobRepository = jobRepository;
_jobRunLogRepository = jobRunLogRepository;
}
public override string Name
{
get { return "jobListener"; }
}
public override async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
{
string group = context.JobDetail.Key.Group;
string name = context.JobDetail.Key.Name;
DateTime fireTimeUtc = TimeZoneInfo.ConvertTimeFromUtc(context.FireTimeUtc.DateTime, TimeZoneInfo.Local);
DateTime? nextFireTimeUtc = null;
if (context.NextFireTimeUtc != null)
{
nextFireTimeUtc = TimeZoneInfo.ConvertTimeFromUtc(context.NextFireTimeUtc.Value.DateTime, TimeZoneInfo.Local);
}
if (!JobHelper.IsBaseJob(group, name))
{
//更新任务执行情况
await _jobRepository.UpdateExecuteAsync(group, name, fireTimeUtc, nextFireTimeUtc);
//记录运行日志
double totalSeconds = context.JobRunTime.TotalSeconds;
bool succ = true;
string exception = string.Empty;
if (jobException != null)
{
succ = false;
exception = jobException.ToString();
}
JobRunLog log = new JobRunLog(group, name, totalSeconds, fireTimeUtc, succ, exception);
await _jobRunLogRepository.InsertAsync(log);
}
}
}
//注册监听器
JobListener listener = serviceProvider.GetService<JobListener>();
_scheduler.ListenerManager.AddJobListener(listener, GroupMatcher<JobKey>.AnyGroup());
六、总结
上述内容只是记录了搭建任务管理平台时的思路和几个关键的点,没有对Quartz.Net基础功能、MongoDB操作做说明,官方文档中包含了完整的说明,官方提供的源码中也有完整的示例,建议阅读官方文档源码来实现更高级的功能。
项目完整代码地址:https://github.com/zhrong92/JobManage
项目截图:

.Net Core实现基于Quart.Net的任务管理的更多相关文章
- .Net Core WebAPI 基于Task的同步&异步编程快速入门
.Net Core WebAPI 基于Task的同步&异步编程快速入门 Task.Result async & await 总结 并行任务(Task)以及基于Task的异步编程(asy ...
- Nagios Core/Icinga 基于栈的缓冲区溢出漏洞
漏洞名称: Nagios Core/Icinga 基于栈的缓冲区溢出漏洞 CNNVD编号: CNNVD-201402-484 发布时间: 2014-03-03 更新时间: 2014-03-03 危害等 ...
- NET Core中基于Generic Host来实现后台任务
NET Core中基于Generic Host来实现后台任务 https://www.cnblogs.com/catcher1994/p/9961228.html 目录 前言 什么是Generic H ...
- .net core 实现基于 cron 表达式的任务调度
.net core 实现基于 cron 表达式的任务调度 Intro 上次我们实现了一个简单的基于 Timer 的定时任务,详细信息可以看这篇文章. 但是使用过程中慢慢发现这种方式可能并不太合适,有些 ...
- ASP.NET Core WebApi基于JWT实现接口授权验证
一.ASP.Net Core WebApi JWT课程前言 我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再 ...
- ASP.NET Core WebApi基于Redis实现Token接口安全认证
一.课程介绍 明人不说暗话,跟着阿笨一起玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NET WebSer ...
- asp.net core 自定义基于 HttpContext 的 Serilog Enricher
asp.net core 自定义基于 HttpContext 的 Serilog Enricher Intro 通过 HttpContext 我们可以拿到很多有用的信息,比如 Path/QuerySt ...
- 基于Spring4的定时任务管理
在项目中,有时会遇到定时任务的处理,下面介绍一下我的做法. 此做法基于Spring4,Spring框架搭建成功,另需引入quartz.jar,pom.xml文件中加入 <dependency&g ...
- 谈谈.NET Core中基于Generic Host来实现后台任务
目录 前言 什么是Generic Host 后台任务示例 控制台形式 消费MQ消息的后台任务 Web形式 部署 IHostedService和BackgroundService的区别 IHostBui ...
随机推荐
- 深入理解HBase
深入理解HBase: https://www.jianshu.com/p/b23800d9b227
- shiro安全框架和spring整合
上干货......... 整合spring的配置文件 <?xml version="1.0" encoding="UTF-8"?><beans ...
- Kafka日志段读写分析
引子 之所以写这篇文章是因为之前面试时候被面试官问到(倒)了,面试官说:"你说你对Kafka比较熟?看过源码? 那说说kafka日志段如何读写的吧?" 我心里默默的说了句 &quo ...
- 简单两步实现Android app 本地设置信息的保存与调用
调用值 SharedPreferences preferences = this.getSharedPreferences("mspda", 0); lblVer.setText( ...
- Django_项目开始
如何初始Django运行环境? 1. 安装python 2. 创建Django项目专用的虚拟环境 http://www.cnblogs.com/2bjiujiu/p/7365876.html 3.进入 ...
- Ubuntu16.04下升级Python到3.7
本帖从IT老兵博客学习得知: 本帖前提: 开发一个Python的系统,需要安装Python3.6以上的版本,由于使用的操作系统是Ubuntu16.04,默认带的Python是2.7.12和3.5,不满 ...
- RTThread DFS文件系统使用: 基于使用SFUD驱动的SPI FLASH之上的ELM FATFS文件系统
参考博文: 博文很长,但是实际要操作的步骤没几下. http://m.elecfans.com/article/730878.html 为了防止几年后文章链接找不到,我把文章复制过来了 /***** ...
- 在IDEA创建类时自动创建作者日期等信息设定
1.效果 1 package com.dream.test; 2 3 /* 4 * @author 匠人码农 5 * @date 2020/04/18 11:17 6 * 概要: 7 * XXXXX ...
- Java知识系统回顾整理01基础03变量01变量定义
定义: 变量:用来命名一个数据的标识符 一.什么是变量? 用具体实例定义变量 变量的定义是: 用来命名一个数据的标识符 1949 这是一个数字,代表某年 如果要命名这个数字,在java里就会写成: i ...
- 怀疑安装MySQL之后,导致OrCAD Capture、Allegro就打不开
记得在异常出现之前,只安装了MySQL,之后OrCAD Capture.Allegro就打不开了. Capture.exe - 系统错误 allegro.exe - 系统错误 我尝试在Cadence的 ...