.Net Core實現基於Quart.Net的任務管理

前段時間給公司項目升級.net框架,把原先的任務管理平臺用.net core實現,現作以下整理:html

1、實現思路

以前的實現也是參考了博客園中其餘文章實現的思路:git

  1. 一個任務定義一個實現IJob接口的類,經過單獨的dll管理;
  2. 經過數據庫持久化、維護任務,便於服務重啓時任務的恢復;
  3. 定義一個管理任務的基礎服務,輪詢數據庫中的任務,根據任務的狀態維護任務的執行;
  4. 新增任務時,須要在數據庫中添加一條記錄,而且在任務管理的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去定義任務。github

2、項目結構

根據上面思路,咱們只須要一個管理任務的基礎服務、一個Web管理平臺就能夠實現,爲了保持項目簡單,把任務管理無關的功能合併在一個項目裏,而且儘可能排除無關的框架和功能點,最終程序包含3個項目:web

  1. JobManage.Service:控制檯程序,管理任務的基礎服務,經過Topshelf部署成windows服務,如何部署參考: http://www.javashuo.com/article/p-vclwtvkf-ht.html
  2. JobManage.Web:Web應用程序,管理平臺,新增、暫停、恢復、刪除任務,查看任務運行日誌;
  3. 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()方法: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();
    }
}

3、任務狀態管理

這裏定義7個任務狀態:待執行、執行中、待暫停、已暫停、待恢復、待刪除、已刪除框架

web管理平臺維護任務(新增、暫停、恢復、刪除)時將任務狀態更新爲待處理狀態(待執行、待暫停、待恢復、待刪除),任務管理基礎服務定時遍歷業務任務,根據數據庫中任務當前的狀態修改任務的執行,而且將數據庫中待處理任務狀態更新爲已處理狀態(執行中、已暫停、已刪除)async

4、任務依賴注入服務

在任務類中咱們用到了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

5、任務監聽

咱們須要記錄任務執行的狀況,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());

6、總結

上述內容只是記錄了搭建任務管理平臺時的思路和幾個關鍵的點,沒有對Quartz.Net基礎功能、MongoDB操做作說明,官方文檔中包含了完整的說明,官方提供的源碼中也有完整的示例,建議閱讀官方文檔源碼來實現更高級的功能。

項目完整代碼地址:https://github.com/zhrong92/JobManage
項目截圖:

相關文章
相關標籤/搜索