Hangfire 

.net core hangfire .net core hangfire和quartz_定时任务

 

用法比较简单,也直接去官网看。这里直接说几种模式的用法。

项目示例

准备工作

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();
    }