Quartz.NET在系统主要承担的一些关键功能:
- 任务调度:Quartz.NET 允许开发人员创建、调度和管理定时任务,支持简单触发器和Cron表达式等多样化的触发策略。
- 灵活性:Quartz.NET 提供了灵活的任务安排机制,不仅支持基于时间间隔的重复执行,还能根据特定日期、时间等信息来安排任务执行。
- 高可用性:对于企业级应用,Quartz.NET 提供了路由支持、Group管理以及持久化机制,确保任务的高可用性和系统的稳定运行。
- 集成性:Quartz.NET 可以与ASP.NET Core应用程序整合,通过Quartz.Extensions.Hosting包实现对后台任务的直接支持。
Quartz.NET 可以在博客系统中承担以下功能:
- 内容发布管理:可以设置定时任务来自动发布文章,例如在特定时间发布博文或定期发布一系列教程。
- 数据备份:可以安排定期执行数据库备份的任务,确保博客数据的安全。
- 定期维护:可以执行如清理垃圾文件、更新插件和系统等定期维护任务。
- 邮件通知:可以实现定时向订阅者发送邮件通知,比如最新文章的推送或者定期通讯。
- 访问统计分析:可以定时生成博客访问报告,分析访问量、用户行为等信息。
- 评论和回复处理:可以设置定时检查评论和回复的任务,对不良信息进行过滤或标记。
- 广告轮换:可以安排广告内容的自动更新和轮换。
- 社交媒体同步:可以安排任务将新内容自动分享到社交媒体平台。
- 反垃圾邮件措施:通过定时监控和清理,减少垃圾评论和留言。
- 用户体验优化:可以通过定期执行A/B测试或其他优化工作来改善用户体验。
- 搜索引擎优化(SEO):定期更新网站的SEO策略和关键字,以提高搜索引擎排名。
- 监控与告警:监控系统性能和可用性,并在出现问题时发送告警。
总的来说,Quartz.NET 的灵活性和强大功能使其成为博客系统中自动化管理任务的理想选择。通过继承
IJob
接口,你可以创建各种类型的作业来满足不同的需求。
这里,我们要处理的就是数据库备份的功能。
1、 安装 NuGet 包: Quartz.NET
2、创建一个作业类:创建一个继承自 IJob
接口的类,实现 Execute
方法,该方法将在指定的时间执行数据库备份操作。
(这里建议在解决方案下边新建一个类库Tasks)
using Quartz;
using System.Threading.Tasks;
public class BackupDatabaseJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
// 在这里执行数据库备份操作
// ...
return Task.CompletedTask;
}
}
3、 配置。 在你的应用程序中,配置 Quartz.NET 以使用你的作业类和触发器。
using Quartz;
using Quartz.Impl;
public static async Task Main()
{
// 创建调度器工厂
StdSchedulerFactory factory = new StdSchedulerFactory();
// 从工厂获取调度器实例
IScheduler scheduler = await factory.GetScheduler();
// 启动调度器
await scheduler.Start();
// 定义作业并将其与我们的作业类关联起来
IJobDetail job = JobBuilder.Create<BackupDatabaseJob>().WithIdentity("backupJob").Build();
// 定义触发器,设置作业执行的时间间隔
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("backupTrigger")
.StartNow() // 立即开始
.WithSimpleSchedule(x => x
.WithIntervalInHours(24) // 每24小时执行一次
.RepeatForever()) // 无限循环
.Build();
// 将作业和触发器添加到调度器中
await scheduler.ScheduleJob(job, trigger);
}
4、运行
5、待测试功能
在执行任务那,我们需要引用其他方法,所以建议单独用一个类库项目存储
--------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------
【二】:续:
下边是项目中使用的简单案例:
1、创建一个帮助类
public class QuartzHelper
{
private static IScheduler _scheduler;
/// <summary>
/// 初始化调度器
/// </summary>
/// <returns></returns>
public static async Task InitializeAsync()
{
if (_scheduler == null)
{
var factory = new StdSchedulerFactory();
_scheduler = await factory.GetScheduler();
await _scheduler.Start();
}
}
public static async Task<string> AddJobAsync<T>(string jobName, string groupName, TimeSpan interval) where T : IJob
{
if (_scheduler == null)
{
await InitializeAsync();
}
//var job = JobBuilder.Create<T>().WithIdentity(jobName, groupName).Build();
//var trigger = TriggerBuilder.Create().WithIdentity($"{jobName}-trigger", groupName)
// .StartNow()
// .WithSimpleSchedule(x => x.WithInterval(interval).RepeatForever())
// .Build();
//替换为:
//反射执行程序集
var jobClassName = typeof(T).Name;
var baseType = typeof(IJob);
var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
var referencedAssemblies = System.IO.Directory.GetFiles(path, "plateau.poetize.blog.Tasks.dll").Select(Assembly.LoadFrom).ToArray();
var jobType = referencedAssemblies
.SelectMany(a => a.DefinedTypes)
.Select(type => type.AsType())
.Where(x => x.Name == jobClassName)
.First();
string times = DateTime.Now.ToString("yyyyMMddHHmmssfff");
//1、任务:
IJobDetail job = new JobDetailImpl(times, groupName, jobType);//任务名称,任务组
//2、触发器
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity(jobName + "-Trigger" + "-" + times, groupName)//触发器名称,任务组
.StartNow() // 立即开始(后续有web界面时,不建议立即执行,每个操作都建议独立)
.WithSimpleSchedule(x => x
.WithIntervalInHours(12) // 每12小时执行一次
.RepeatForever()) // 无限循环
.Build();
//3、参数(可无)
job.JobDataMap.Add("JobParam", "参数:......");//共享的,也可以更改值
job.JobDataMap.Add("JobRunNum", 1);//具体执行的时候可以更改值,打印
//4、调度器
var v = await _scheduler.ScheduleJob(job, trigger);
Console.WriteLine("任务开始成功");
return "任务开始成功";
}
public static async Task RemoveJobAsync(string jobName, string groupName)
{
if (_scheduler == null)
{
return;
}
await _scheduler.DeleteJob(new JobKey(jobName, groupName));
}
public static async Task StartAsync()
{
if (_scheduler == null)
{
await InitializeAsync();
}
await _scheduler.Start();
}
public static async Task StopAsync()
{
if (_scheduler == null)
{
return;
}
await _scheduler.Shutdown();
}
}
2、Job任务类:
public class BackupDatabaseJob : IJob
{
/// <summary>
/// 执行
/// </summary>
/// <param name="context">上下文</param>
/// <returns></returns>
public Task Execute(IJobExecutionContext context)
{
using (var serviceScope = MyApp.Instance.CreateScope())
{
var server = serviceScope.ServiceProvider.GetService<IMyJobService>();
var list = server.BackupDatabase();
//获取传递过来的参数
JobDataMap data = context.JobDetail.JobDataMap;
int jobRunNum = data.GetInt("JobRunNum");
string jobParam = data.GetString("JobParam");
///
Console.WriteLine($"BackupDatabaseJob 执行 {DateTime.Now.ToString()}");
Console.WriteLine($"【JobRunNum】 :{jobRunNum}");
Console.WriteLine($"【JobParam】: 执行 {jobParam}");
修改值
//jobRunNum++;
//data["JobRunNum"] = jobRunNum; // 修改 JobDataMap 中的值
}
return Task.CompletedTask;
}
}
上边与之前的差距主要就是在使用
//引入命名空间(nuget包)
using Microsoft.Extensions.DependencyInjection;
//代码
using (var serviceScope = MyApp.Instance.CreateScope())
这里的MyApp是一个静态类
public static class MyApp
{
public static IServiceProvider Instance { get; set; }
}
这里值的赋值在入口文件中进行:
//写入静态类供全局获取
MyApp.Instance = app.Services;
3、注入服务:
//注入服务
builder.Services.AddTransient<IMyJobService, MyJobService>();
builder.Services.AddTransient<ISchedulerCenter, SchedulerCenterServer>();
//注入任务
builder.Services.AddTransient(typeof(BackupDatabaseJob));
注入服务可以反射循环注入。
4、运行:
await QuartzHelper.AddJobAsync<BackupDatabaseJob>("myJob_BackupDatabaseJob", "myGroup_BackupDatabaseJob", TimeSpan.FromHours(24));
5、效果:
6、拓展:
6.1、目前使用的方式都是代码固定的,可创建模型进行参数化
6.2、后续还要做一个Web的可视化操作。
6.3、因为异步,如果多层可能会得不到实际的返回值。
6.4、共享参数的读取、赋值。记录日志等
6.5、并发问题:(例如程序需要执行2秒,但是任务是1秒一次)。实际的执行时间大于给定任务时间。需要设置,然后会根据实际的任务时间进行循环。