1、常常在項目會用到定時任務同步數據或更新緩存等操做,在好久之前咱們可能常常會用一個多線程或timer來作定時任務,這樣能實現比較簡單輕量級的任務;對於任務多且都調用頻率不同的任務,咱們都會用到Quartz.Net這個組件;git
Quartz.NET是一個強大、開源、輕量的做業調度框架,你可以用它來爲執行一個做業而建立簡單的或複雜的做業調度。它有不少特徵,如:數據庫支持,集羣,插件,支持cron-like表達式等等github
2、 接下來簡單演示一下Quartz使用:數據庫
2.1 首先新建一個AspNet Core API 項目,經過nuget包管理器安裝引用Quartz緩存
2.2 新建一個模擬任務類UserInfoSyncjob 必須繼承IJob接口多線程
namespace QuartzDemo.Quarzs { public class UserInfoSyncjob : IJob { public Task Execute(IJobExecutionContext context) { return Task.Run(() => { //..... Console.WriteLine($"{DateTime.Now.ToString()}:開始執行同步第三方數據"); //....同步操做 }); } } }
2.2 聲明一個啓動類QuartzStartup,來控制任務啓動關閉等方法app
添加啓動方法框架
public async Task<string> Start() { //一、聲明一個調度工廠 _schedulerFactory = new StdSchedulerFactory(); //二、經過調度工廠得到調度器 _scheduler = await _schedulerFactory.GetScheduler(); //三、開啓調度器 await _scheduler.Start(); //四、建立一個觸發器 var trigger = TriggerBuilder.Create() .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever())//每兩秒執行一次 .Build(); //五、建立任務 var jobDetail = JobBuilder.Create<UserInfoSyncjob>() .WithIdentity("job", "group") .Build(); //六、將觸發器和任務器綁定到調度器中 await _scheduler.ScheduleJob(jobDetail, trigger); return await Task.FromResult("將觸發器和任務器綁定到調度器中完成"); }
2.3 在網站啓動完成時調用QuartzStartup的Start方法開啓任務async
先注入 Quartz調度類ide
添加網站啓動開始方法學習
2.四、運行效果,運行以前將控制檯開啓(方便查看任務是否在執行,實際環境可寫日誌)
該調度任務完成,上方定義的觸發器是2秒一次,因此該任務每隔2秒執行;(也能夠經過配置文件,控制執行平率,cron表達式能夠很好控制)
3、第二結簡單演示了Quartz的基本用法,本文重點不是主要講解Quartz的用法,上方只是爲了沒使用過Quartz的同行有個簡單映像,若是想詳細學習,博客園有不少相似的文章,也能夠和我探討一下!
本文重點是每一個任務類怎麼經過注入獲取其餘類的使用及參數配置類等等;
假若有這樣一個需求,UserInfoSyncjob同步任務裏面須要配置數據庫鏈接參數和日誌記錄、緩存記錄等,在以前咱們可能經過配置類、日誌類、緩存類以工廠形式單例建立獲取。
在AspNet Core自帶IOC容器框架,不少配置類、日誌類、緩存類等等,在全局不少地方都會使用,咱們如今作法就是把這些類注入到IOC容器中,若是須要的只須要從構造方法中獲取;
咱們都知道若是一個從構造方法中獲取IOC容器裏面的類型實例,必須該類型也要主要到IOC容器中,這樣咱們就要想辦法把UserInfoSyncjob經過容器來建立生產;
經過源碼發如今Quartz有一個默認的生成job的工廠類Quartz.Simpl.SimpleJobFactory
using System; using Quartz.Logging; using Quartz.Spi; using Quartz.Util; namespace Quartz.Simpl { /// <summary> /// The default JobFactory used by Quartz - simply calls /// <see cref="ObjectUtils.InstantiateType{T}" /> on the job class. /// </summary> /// <seealso cref="IJobFactory" /> /// <seealso cref="PropertySettingJobFactory" /> /// <author>James House</author> /// <author>Marko Lahma (.NET)</author> public class SimpleJobFactory : IJobFactory { private static readonly ILog log = LogProvider.GetLogger(typeof (SimpleJobFactory)); /// <summary> /// Called by the scheduler at the time of the trigger firing, in order to /// produce a <see cref="IJob" /> instance on which to call Execute. /// </summary> /// <remarks> /// It should be extremely rare for this method to throw an exception - /// basically only the case where there is no way at all to instantiate /// and prepare the Job for execution. When the exception is thrown, the /// Scheduler will move all triggers associated with the Job into the /// <see cref="TriggerState.Error" /> state, which will require human /// intervention (e.g. an application restart after fixing whatever /// configuration problem led to the issue with instantiating the Job). /// </remarks> /// <param name="bundle">The TriggerFiredBundle from which the <see cref="IJobDetail" /> /// and other info relating to the trigger firing can be obtained.</param> /// <param name="scheduler"></param> /// <returns>the newly instantiated Job</returns> /// <throws> SchedulerException if there is a problem instantiating the Job. </throws> public virtual IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { IJobDetail jobDetail = bundle.JobDetail; Type jobType = jobDetail.JobType; try { if (log.IsDebugEnabled()) { log.Debug($"Producing instance of Job '{jobDetail.Key}', class={jobType.FullName}"); } return ObjectUtils.InstantiateType<IJob>(jobType); } catch (Exception e) { SchedulerException se = new SchedulerException($"Problem instantiating class '{jobDetail.JobType.FullName}'", e); throw se; } } /// <summary> /// Allows the job factory to destroy/cleanup the job if needed. /// No-op when using SimpleJobFactory. /// </summary> public virtual void ReturnJob(IJob job) { var disposable = job as IDisposable; disposable?.Dispose(); } } }
SimpleJobFactory 實現了IJobFactory接口,經過源碼發現咱們若是要替換該工廠來控制job的生成,只須要建立一個IOCJobFactory來替換默認job工廠就行
public class IOCJobFactory : IJobFactory { private readonly IServiceProvider _serviceProvider; public IOCJobFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { return _serviceProvider.GetService(bundle.JobDetail.JobType) as IJob; } public void ReturnJob(IJob job) { var disposable = job as IDisposable; disposable?.Dispose(); } }
在調度任務類裏面從新設置job工廠 _scheduler.JobFactory = _iocJobfactory;
在IOC中注入 UserInfoSyncjob、StdSchedulerFactory、IOCJobFactory
services.AddTransient<UserInfoSyncjob>(); // 這裏使用瞬時依賴注入 services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();//註冊ISchedulerFactory的實例。 services.AddSingleton<QuartzStartup>(); services.AddSingleton<IJobFactory,IOCJobFactory>();
修改UserInfoSyncjob任務類,能夠經過構造方法注入的方式從容器中拿到日誌實現類、緩存類等等
public class UserInfoSyncjob : IJob { private readonly ILogger<UserInfoSyncjob> _logger; // private readonly ICache _cache; public UserInfoSyncjob(ILogger<UserInfoSyncjob> logger) { //_cache = cache; _logger = logger;// EnginContext.Current.Resolve<ILogger<UserInfoSyncjob>>(); } public Task Execute(IJobExecutionContext context) { return Task.Run(() => { //..... // Console.WriteLine($"{DateTime.Now.ToString()}:開始執行同步第三方數據"); _logger.LogInformation ($"{DateTime.Now.ToString()}:開始執行同步第三方數據"); //....同步操做 // 咱們都知道若是一個從構造方法中獲取IOC容器裏面的類型,必須該類型也要主要到IOC容器中; }); } }
調整後運行截圖
具體詳細步驟請看源碼:https://github.com/lxshwyan/QuartzDemo.git