前段時間給公司項目升級.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
項目截圖: