前段时间给公司项目升级.net框架,把原先的任务管理平台用.net core实现,现作以下整理:html
以前的实现也是参考了博客园中其余文章实现的思路:git
因为业务代码会频繁调整,咱们业务代码从任务执行中拆分出来,独立部署成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去定义任务。github
根据上面思路,咱们只须要一个管理任务的基础服务、一个Web管理平台就能够实现,为了保持项目简单,把任务管理无关的功能合并在一个项目里,而且尽可能排除无关的框架和功能点,最终程序包含3个项目:web
动态添加任务:数据库
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()方法:windows
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管理平台维护任务(新增、暂停、恢复、删除)时将任务状态更新为待处理状态(待执行、待暂停、待恢复、待删除),任务管理基础服务定时遍历业务任务,根据数据库中任务当前的状态修改任务的执行,而且将数据库中待处理任务状态更新为已处理状态(执行中、已暂停、已删除)async
在任务类中咱们用到了http服务,咱们须要在任务类中获取http服务,咱们经过.Net Core注入和获取服务的方式来实现,这里主要是要自定义任务类实例的建立和获取,官方文档( https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/miscellaneous-features.html#jobfactory )中说明能够经过实现 IJobFactory 接口,而且修改 IScheduler.JobFactory的属性来实现:ide
//自定义任务实例获取 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-factoriesui
咱们须要记录任务执行的状况,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
项目截图: