.Net Core 之 (Hangfire 和 Quertz)定时任务
Hangfire
用法比较简单,也直接去官网看。这里直接说几种模式的用法。
项目示例
准备工作
1. 引入nuget包
Hangfire.AspNetCore Hangfire.Dashboard.BasicAuthorization #用于Dashboard面板 Hangfire.SqlServer #我这里用的sqlserver,如果用其他的数据库存储就换成对应的扩展包
2. appsettings.json中添加配置
"HangfireConfig": { "SslRedirect": false, "RequireSsl": false, "LoginCaseSensitive": false, "Login": "fcbadmin", "PasswordClear": "123456", "ConnectionString": "Server=.\\sqlexpress;Database=HangfireTest;Integrated Security=SSPI;" }
3. Program中添加服务注入配置
builder.Services.AddHangfire(configuration => configuration //.SetDataCompatibilityLevel(CompatibilityLevel.Version_170) .UseSimpleAssemblyNameTypeSerializer() .UseRecommendedSerializerSettings() .UseSqlServerStorage(builder.Configuration.GetSection("HangfireConfig:ConnectionString").Value, new SqlServerStorageOptions { CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), QueuePollInterval = TimeSpan.Zero, UseRecommendedIsolationLevel = true, DisableGlobalLocks = true })); builder.Services.AddHangfireServer();
或者换种简单的
builder.Services.AddHangfire(config => { config.UseStorage(new SqlServerStorage(Configuration.GetSection("HangfireConfig").GetValue<string>("ConnectionString"))); });
4. Program中添加Dashboard面板,这里添加了账号认证,如果觉得没有必要把配置取消就可以了
app.UseHangfireDashboard("/task", new DashboardOptions { Authorization = new[] { new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions{ SslRedirect = builder.Configuration.GetSection("HangfireConfig").GetValue<bool>("SslRedirect"), RequireSsl=builder.Configuration.GetSection("HangfireConfig").GetValue<bool>("RequireSsl"), LoginCaseSensitive=builder.Configuration.GetSection("HangfireConfig").GetValue<bool>("LoginCaseSensitive"), Users=new []{ new BasicAuthAuthorizationUser{ Login=builder.Configuration.GetSection("HangfireConfig").GetValue<string>("Login"), PasswordClear=builder.Configuration.GetSection("HangfireConfig").GetValue<string>("PasswordClear") } } }) }, });
准备工作完成了,下面就看几种模式用法
定时任务
/// <summary> /// 执行定时任务。定期作业按指定的 CRON 计划触发多次。 /// </summary> /// <returns></returns> [HttpGet] [Route("Test_RecurringJob")] public async Task<IActionResult> Test_RecurringJob() { //自动获取本地时区 RecurringJob.AddOrUpdate<ValuesController>("测试定时任务", x => x.TestRecurringJobContent(), Cron.MinuteInterval(10), TimeZoneInfo.Local); ////指定Windows时区 //RecurringJob.AddOrUpdate<ValuesController>("测试定时任务", x => x.TestRecurringJobContent(), Cron.MinuteInterval(2), TimeZoneInfo.CreateCustomTimeZone("China Standard Time", new TimeSpan(08, 00, 00), "China Standard Time", "China Standard Time")); ////指定Linux时区 //RecurringJob.AddOrUpdate<ValuesController>("测试定时任务", x => x.TestRecurringJobContent(), Cron.MinuteInterval(2), TimeZoneInfo.CreateCustomTimeZone("Asia/Shanghai", new TimeSpan(08, 00, 00), "Asia/Shanghai", "Asia/Shanghai")); return Ok("执行成功"); } /// <summary> /// 执行定时任务 /// </summary> /// <returns></returns> public async Task<IActionResult> TestRecurringJobContent() { Console.WriteLine("执行了定时任务"); return Ok(); }
一次性作业
/// <summary> /// 立即执行一次性作业。即发即弃作业仅在创建后立即执行一次。 /// </summary> /// <returns></returns> [HttpGet] [Route("Test_BackgroundJob_Enqueue")] public async Task<IActionResult> Test_BackgroundJob_Enqueue() { var jobId = BackgroundJob.Enqueue( () => Console.WriteLine("执行了立即执行一次性作业")); return Ok("执行成功"); }
延迟作业
/// <summary> /// 延迟作业。延迟的作业也只执行一次,但不会在一定时间间隔后立即执行。 /// </summary> /// <returns></returns> [HttpGet] [Route("Test_BackgroundJob_Schedule")] public async Task<IActionResult> Test_BackgroundJob_Schedule() { var jobId = BackgroundJob.Schedule( () => Console.WriteLine("执行了延迟作业"), TimeSpan.FromMinutes(1)); return Ok("执行成功"); }
延续作业
/// <summary> /// 延续作业。延续在其父作业完成时执行。 /// </summary> /// <returns></returns> [HttpGet] [Route("Test_BackgroundJob_ContinueJobWith")] public async Task<IActionResult> Test_BackgroundJob_ContinueJobWith() { Console.WriteLine("开始等待,当前时间:" + DateTime.Now); var jobId = BackgroundJob.Schedule( () => Console.WriteLine("执行了2秒延迟作业,当前时间:"+DateTime.Now), TimeSpan.FromSeconds(2)); BackgroundJob.ContinueJobWith( jobId, () => Console.WriteLine("在执行了2秒延迟作业之后,又执行了延续作业,当前时间:" + DateTime.Now)); return Ok("执行成功"); }
Quartz
Quartz.Net 官网:https://www.quartz-scheduler.net/
个人感觉没有hangfire好用,使用要稍微复杂一些,官方没有看到类似于hangfire中Dashboard的可视化界面,网上倒是很多人扩展了,可以直接用
核心接口
Scheduler - 与调度程序交互的主要API。 Job - 你想要调度器执行的任务组件需要实现的接口 JobDetail - 用于定义作业的实例。 Trigger(即触发器) - 定义执行给定作业的计划的组件。 JobBuilder - 用于定义/构建 JobDetail 实例,用于定义作业的实例。 TriggerBuilder - 用于定义/构建触发器实例。 Scheduler 的生命期,从 SchedulerFactory 创建它时开始,到 Scheduler 调用shutdown() 方法时结束;Scheduler 被创建后,可以增加、删除和列举 Job 和 Trigger,以及执行其它与调度相关的操作(如暂停 Trigger)。但是,Scheduler 只有在调用 start() 方法后,才会真正地触发 trigger(即执行 job)
准备工作
1. 引入nuget包
Quartz
2. Program中添加服务注入配置,这里没有集成数据库
builder.Services.AddScoped<ISchedulerFactory, StdSchedulerFactory>();
3. api中依赖注入
private readonly ISchedulerFactory _schedulerFactory; private readonly IScheduler _scheduler; public ValuesController( ISchedulerFactory schedulerFactory) { _schedulerFactory = schedulerFactory; _scheduler = _schedulerFactory.GetScheduler().Result; //通过工场类获得调度器 }
简单使用
public async Task<IActionResult> RecurringJob() { //开启调度器 await _scheduler.Start(); //创建触发器(也叫时间策略) var trigger = TriggerBuilder.Create() // .WithSimpleSchedule(x => x.WithIntervalInMinutes(1).RepeatForever())//每分钟执行一次,一直重复执行 //.WithSimpleSchedule(x => x.WithIntervalInSeconds(2).WithRepeatCount(5))//每2秒执行一次,重复执行五次 .WithCronSchedule("0/2 * * * * ?") //使用cron指定运行时间来执行,每2秒运行一次 // .WithIdentity("fcbjob","fcbgroup") .Build(); //创建作业实例 //Jobs即我们需要执行的作业 var jobDetail = JobBuilder.Create<FCBJob>() .WithIdentity("Myjob", "fcbgroup")//我们给这个作业取了个“Myjob”的名字,取了个组名为“fcbgroup”,这里会通过这两个名字来生成唯一的jobkey,如果不指定会默认生成一个唯一jobkey .Build(); await _scheduler.ScheduleJob(jobDetail, trigger); return Ok("执行成功"); }
一个Job执行多个触发器
job和trigger 可以是一对多的关系
public async Task<IActionResult> RecurringJobs() { //开启调度器 await _scheduler.Start(); //创建触发器(也叫时间策略) var trigger = TriggerBuilder.Create() .WithCronSchedule("0/2 * * * * ?") //使用cron指定运行时间来执行,每2秒运行一次 .WithIdentity("fcbjob","fcbgroup") .Build(); var trigger2 = TriggerBuilder.Create() .WithCronSchedule("0/1 * * * * ?") //使用cron指定运行时间来执行,每2秒运行一次 .WithIdentity("fcbjob2", "fcbgroup") .Build(); var jobDetail2 = JobBuilder.Create<FCBJob2>() .WithIdentity("Myjob2", "fcbgroup") .Build(); Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>> keyValuePairs = new Dictionary<IJobDetail, IReadOnlyCollection<ITrigger>>(); keyValuePairs.Add(jobDetail2, new List<ITrigger>() { trigger,trigger2 }); await _scheduler.ScheduleJobs(keyValuePairs,true); return Ok("执行成功"); }
停止任务调度
public async Task<IActionResult> ShutdownScheduler() {
await _scheduler.Shutdown();
return Ok();
}
暂停定时任务
public async Task<IActionResult> PauseJob() { //暂停job //JobKey jobKey = new JobKey("Myjob", "fcbgroup"); //if (await _scheduler.CheckExists(jobKey)) //{ // await _scheduler.PauseJob(jobKey); //} //暂停triggerKey TriggerKey triggerKey = new TriggerKey("fcbjob", "fcbgroup"); if (await _scheduler.CheckExists(triggerKey)) { await _scheduler.PauseTrigger(triggerKey); } return Ok(); }
恢复定时任务
public async Task<IActionResult> ResumeJob() { //恢复job //JobKey jobKey = new JobKey("Myjob", "fcbgroup"); //if (await _scheduler.CheckExists(jobKey)) //{ // await _scheduler.ResumeJob(jobKey); //} //恢复triggerKey TriggerKey triggerKey = new TriggerKey("fcbjob", "fcbgroup"); if (await _scheduler.CheckExists(triggerKey)) { await _scheduler.ResumeTrigger(triggerKey); } return Ok(); }
删除定时任务
public async Task<IActionResult> DeleteJob() { JobKey jobKey = new JobKey("Myjob", "fcbgroup"); if (await _scheduler.CheckExists(jobKey)) { await _scheduler.DeleteJob(jobKey); } return Ok(); }