源碼地址: https://github.com/246850/Calamus.TaskScheduler
演示地址:http://47.101.47.193:1063/

1、Quartz.NET框架核心類
IScheduler:調度者
IJobDetail:任務
ITrigger:觸發器
JobKey:任務/觸發器標識
JobDataMap:數據包
2、郵件通知(FluentEmail類庫)
_fluentEmail.To(to).Subject(subject).Body(body, true).SendAsync();
3、Quartz.NET宿主方式,依靠IHostedService后台服務執行
internal class QuartzHostedService : IHostedService
{
private readonly ISchedulerFactory schedulerFactory;
private readonly IOptions<QuartzHostedServiceOptions> options;
private IScheduler scheduler = null!;
public QuartzHostedService(
ISchedulerFactory schedulerFactory,
IOptions<QuartzHostedServiceOptions> options)
{
this.schedulerFactory = schedulerFactory;
this.options = options;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
scheduler = await schedulerFactory.GetScheduler(cancellationToken);
await scheduler.Start(cancellationToken);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return scheduler.Shutdown(options.Value.WaitForJobsToComplete, cancellationToken);
}
}
4.Asp.Net Core 5.0集成
安裝依賴包
Quartz
Quartz.AspNetCore
Quartz.Plugins.TimeZoneConverter
Quartz.Serialization.Json
FluentEmail.Core
FluentEmail.Smtp
Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add<GatewayResultFilterAttribute>(); // 通用執行結果包裝處理過濾器
options.Filters.Add<GlobalExceptionFilterAttribute>(); // 全局異常過濾器
})
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter()); // 日期格式化
})
.AddFluentValidation(config => // 請求模型參數驗證
{
config.RunDefaultMvcValidationAfterFluentValidationExecutes = true; // false : 禁止默認模型驗證
config.ValidatorOptions.CascadeMode = CascadeMode.Stop; // 不級聯驗證,第一個規則錯誤就停止
config.RegisterValidatorsFromAssemblyContaining<JobCreateOrUpdateValidator>();
});
services.AddHostedService<NLogHostService>(); // NLog 關閉服務
services.AddDistributedMemoryCache(); // 分布式緩存接口
services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));// 解決中文亂碼
services.AddHttpClient(); // IHttpClientFactory
IConfigurationSection quartzConfiguration = Configuration.GetSection("Quartz"); // Quartz配置節點
/***********Quartz.NET*********/
services.AddTransient<HttpJob>(); // 注冊job至容器,必須步驟
services.AddQuartz(config =>
{
config.UseTimeZoneConverter();
// 使用MicrosoftDependencyInjectionJobFactory工廠類從 容器 中創建job實例
config.UseMicrosoftDependencyInjectionJobFactory(options =>
{
options.AllowDefaultConstructor = false; // 禁止使用無參構建函數創建 job
options.CreateScope = false;
});
config.UseDefaultThreadPool(options =>
{
options.MaxConcurrency = 10; // 最大並發執行線程數
});
config.UsePersistentStore(options =>
{
options.UseProperties = false;
//options.UseBinarySerializer(); // 二進制序列化
options.UseJsonSerializer(); // json序列化
options.UseMySql(ado =>
{
ado.ConnectionString = quartzConfiguration["Database"];
ado.TablePrefix = quartzConfiguration["TablePrefix"]; // 默認值 QRTZ_
ado.ConnectionStringName = "Quartz.net";
});
});
// 監聽器
config.AddSchedulerListener<DefaultSchedulerListener>();
config.AddJobListener<DefaultJobListener>();
config.AddTriggerListener<DefaultTriggerListener>();
// 啟動NLog日志文件清除job
config.ScheduleJob<ClearNLogJob>(trigger =>
{
trigger.WithIdentity(NLogJobKey.NameKey, NLogJobKey.GroupKey).StartNow()
.WithCronSchedule("0 0 0 1/3 * ? ", cron => cron.WithMisfireHandlingInstructionFireAndProceed()); // 從每月1日開始,每3天執行一次
}, job =>
{
job.WithIdentity(NLogJobKey.NameKey, NLogJobKey.GroupKey)
.StoreDurably(false) // 是否持久化, 無關聯觸發器時是否移除,false:移除
.RequestRecovery() // 重啟后是否恢復任務
.WithDescription("每3天清空NLog日志文件");
});
});
// IHostedService宿主啟動 Quartz服務 services.AddSingleton<IHostedService, QuartzHostedService>()
services.AddQuartzServer(options =>
{
// when shutting down we want jobs to complete gracefully
options.WaitForJobsToComplete = true; // 等待任務執行完,再退出
});
/***********FluentEmail*********/
// 為了將郵件通知配置在job data上, 不使用自帶的service注冊方式
//services.AddFluentEmail(quartzConfiguration["Smtp:UserName"], "Quartz.NET任務調度通知")
// .AddRazorRenderer()
// .AddSmtpSender(quartzConfiguration["Smtp:Host"], Convert.ToInt32(quartzConfiguration["Smtp:Port"]), quartzConfiguration["Smtp:UserName"], quartzConfiguration["Smtp:Password"]);
services.AddTransient<IFluentEmail>(serviceProvider =>
{
IScheduler scheduler = serviceProvider.GetRequiredService<ISchedulerFactory>().GetScheduler().Result;
JobKey key = new JobKey(EmailJobKeys.NameKey, EmailJobKeys.GroupKey);
if (!scheduler.CheckExists(key).Result)
{
JobDataMap dataMap = new JobDataMap();
dataMap.Put(EmailJobKeys.Host, "smtp.qq.com");
dataMap.Put(EmailJobKeys.Port, 587); // 465端口一直嘗試不通過,奇怪
dataMap.Put(EmailJobKeys.UserName, "390915549@qq.com"); // 作者qq,歡迎騷擾
dataMap.Put(EmailJobKeys.Password, "cirxjtemuzxycagf");
dataMap.Put(EmailJobKeys.To, string.Empty); // 接收者郵件支持多個,以 ; 隔開
dataMap.Put(EmailJobKeys.NickName, "Quartz.NET任務調度通知");
dataMap.Put(EmailJobKeys.CacheExpiry, 30); // 默認30分鍾內只通知一次
IJobDetail job = JobBuilder.Create<HttpJob>()
.StoreDurably(true)
.RequestRecovery()
.WithDescription("郵件通知配置Job,切勿刪除")
.WithIdentity(key)
.UsingJobData(dataMap)
.Build();
scheduler.AddJob(job, true); // 初始化郵件通知配置
}
IJobDetail emailJob = scheduler.GetJobDetail(key).Result;
IFluentEmail fluentEmail = new Email(new ReplaceRenderer(),
new SmtpSender(new SmtpClient(emailJob.JobDataMap.GetString(EmailJobKeys.Host), emailJob.JobDataMap.GetInt(EmailJobKeys.Port))
{
EnableSsl = true,
Credentials = new NetworkCredential(emailJob.JobDataMap.GetString(EmailJobKeys.UserName),
emailJob.JobDataMap.GetString(EmailJobKeys.Password))
}),
emailJob.JobDataMap.GetString(EmailJobKeys.UserName),
emailJob.JobDataMap.GetString(EmailJobKeys.NickName));
return fluentEmail;
});
IocEngine.Instance.Init(services); // 實在沒辦法才弄個靜態容器獲取service, 監聽器里無法通過構造函數 注入 ISchedulerFactory, IFluentEmail, 猜測應該是循環引用了
}

5、謝謝觀看,拜拜
