在上篇文章中Quartz.Net(1) 已經介紹了Quartz.Net的基本運用,該篇文章中將主要介紹NetCore3.1如何整合Quartz.Net,在后臺運行定時job,并運用到上篇文章講到的介紹點。
1 導入Nuget包
<PackageReference Include="Quartz" Version="3.8.1" /><PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.8.1" /><PackageReference Include="Quartz.Extensions.Hosting" Version="3.8.1" />
Quartz 定時框架
Quartz.Extensions.DependencyInjection 用于在任務中導入其他的服務,整合使用NetCore的依賴注入框架
Quartz.Extensions.Hosting 使得Job在后臺運行
.NetCore3.1 整合的版本均是3.8.1
2 定義Job
在定義Job時,用到了一個自定義特性JobAttribute,標注在實現了 I J o b \textcolor{red}{IJob} IJob類上,標明該任務的分組,名稱,描述 ,觸發器觸發時的Corn表達式。
在實際運行的任務中將依賴于其他服務 如數據庫服務、配置服務等,這里可以通過構造函數注入的方式引入其他的服務
/// <summary>/// 自定義特性,用來標注Job運行信息/// </summary>[AttributeUsage(AttributeTargets.Class)]public class JobAttribute: Attribute{/// <summary>/// Job運行的Corn表達式/// </summary>public string Corn { get; set; }/// <summary>/// Job組/// </summary>public string JobGroup { get; set; }/// <summary>/// Job名稱/// </summary>public string JobName { get; set; }/// <summary>/// Job描述/// </summary>public string JobDesc { get; set; }public JobAttribute(string Corn) {this.Corn = Corn;}}
/// <summary>/// 新進員工創建用戶刷臉照片Job /// 每天6點、14點執行/// </summary>[Job("0 0 6,14 * * ? *", JobDesc = "新進員工創建用戶刷臉照片Job", JobGroup = "group2", JobName = "AddJob")]public class CreatePhotoJob : IJob{private FaceContext faceContext;private FaceWebService faceWebService;private ILogger<DelLeftEmpPhotoJob> logger;private IConfiguration configuration;public CreatePhotoJob(FaceContext faceContext, FaceWebService faceWebService, ILogger<DelLeftEmpPhotoJob> logger, IConfiguration configuration){this.faceContext = faceContext;this.faceWebService = faceWebService;this.logger = logger;this.configuration = configuration;}public async Task Execute(IJobExecutionContext context){//忽略任務的具體執行邏輯await RunJob();}}
/// <summary>/// 刪除離職人員刷臉照片Job/// 凌晨2點鐘執行/// </summary>[Job("0 0 2 * * ? *", JobDesc = "刪除離職人員刷臉照片Job",JobGroup ="group1",JobName ="DelJob")]public class DelLeftEmpPhotoJob : IJob{private FaceContext faceContext;private FaceWebService faceWebService;private ILogger<DelLeftEmpPhotoJob> logger;public DelLeftEmpPhotoJob(FaceContext faceContext, FaceWebService faceWebService, ILogger<DelLeftEmpPhotoJob> logger){this.faceContext = faceContext;this.faceWebService = faceWebService;this.logger = logger;}public async Task Execute(IJobExecutionContext context){//忽略任務的具體執行邏輯await RunJob();}
}
3 定義JobListener
為了更好的監聽任務的運行狀態,比如說在任務運行結束時向開發人員推送郵件,這里用到了任務監聽器。實現方式時實現一個IJobListener。具體的業務邏輯請忽略,這里只是給出一個思路。
/// <summary>/// 任務監聽器,使用AOP的思想監聽任務[IJob]運行前后的動作/// </summary>public class MyJobListener : IJobListener{public string Name => "JobListener";public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default){//throw new NotImplementedException();}/*** 監聽Job運行前*/public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default){// throw new NotImplementedException();}/*** 在這里執行任務結束后的操作* 根據JobDetail所在的分組與名稱分別發送不同內容的郵件信息*/public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default){try{HRWebService webService = new HRWebService();var jobkey = context.JobDetail.Key;MailBody body = null ;if (jobkey.Group.Equals("group2") && jobkey.Name.Equals("AddJob")){body = MailBody.AddJobMail();}else if (jobkey.Group.Equals("group1") && jobkey.Name.Equals("DelJob")){body = MailBody.DelJobMail();}await webService.SendMail(body);}catch (Exception e) { }}}
3 配置服務
為了運行定義的IJob 示例,需要注冊服務,觸發器,監聽器等信息,通常是在startup中進行配置。為了避免在startup中進行關于Quartz復雜的配置,這里將Job配置,觸發器的配置等配置服務抽離到一個單獨的配置類中,運用到了反射的思想。
/// <summary>/// Quartz.Net 定時Job配置類/// </summary>public static class QuartzConfiguration{public static IServiceCollection AddMyQuartz(this IServiceCollection services) {//讀取所有實現IJob接口的類,類上標注了JobAttribute類,調度器使用corn觸發器去觸發JOB任務//FaceJob是我定義Job的模塊,這里只做參考,實際讀取IJob的反射Type可能各不相同AssemblyName assemblyName = new AssemblyName("FaceJob");Assembly anotherModuleAssembly = Assembly.Load(assemblyName);var types = anotherModuleAssembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IJob))).Where(t => t.GetCustomAttribute<JobAttribute>() != null).ToArray();services.AddQuartz(configure =>{//Quartz3.8.1版本 默認是使用NetCore的依賴注入框架的,因此可以不用顯示的定義此信息,但是在3.3.2及之前版本,不顯示定義此配置是無法在Job中引入其他服務的//configure.UseMicrosoftDependencyInjectionJobFactory();foreach (Type type in types){//讀取IJob上定jobAttribute(標注Job運行的信息) JobAttribute jobAttribute = type.GetCustomAttribute<JobAttribute>();var jobKey = new JobKey(jobAttribute.JobName, jobAttribute.JobGroup);//配置Jobconfigure.AddJob(type, jobKey, config =>{config.WithDescription(jobAttribute.JobDesc);});//配置觸發器,統一使用Corn觸發器configure.AddTrigger(config =>{config.ForJob(jobKey).WithCronSchedule(jobAttribute.Corn);});}//配置job監聽器[監聽任意組下的Job]configure.AddJobListener<MyJobListener>(GroupMatcher<JobKey>.AnyGroup());});//后臺Job,任務在后臺運行services.AddQuartzHostedService(options =>{options.WaitForJobsToComplete = true;});return services;} }
在startup中引入Quartz配置
public void ConfigureServices(IServiceCollection services){//忽略其他服務配置#region Quartz服務配置services.AddMyQuartz();#endregion}