.Net Core 之 (Hangfire 和 Quertz)定时任务

Hangfire 

 

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

项目示例

准备工作

1. 引入nuget包

1
2
3
4
5
Hangfire.AspNetCore
 
Hangfire.Dashboard.BasicAuthorization  #用于Dashboard面板
 
Hangfire.SqlServer  #我这里用的sqlserver,如果用其他的数据库存储就换成对应的扩展包

2. appsettings.json中添加配置

1
2
3
4
5
6
7
8
"HangfireConfig": {
  "SslRedirect": false,
  "RequireSsl": false,
  "LoginCaseSensitive": false,
  "Login": "fcbadmin",
  "PasswordClear": "123456",
  "ConnectionString": "Server=.\\sqlexpress;Database=HangfireTest;Integrated Security=SSPI;"
}

3.  Program中添加服务注入配置

1
2
3
4
5
6
7
8
9
10
11
12
13
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();

或者换种简单的

1
2
3
4
builder.Services.AddHangfire(config =>
{
    config.UseStorage(new SqlServerStorage(Configuration.GetSection("HangfireConfig").GetValue<string>("ConnectionString")));
});

4. Program中添加Dashboard面板,这里添加了账号认证,如果觉得没有必要把配置取消就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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")
                             }
                         }
                    })
                },
});

准备工作完成了,下面就看几种模式用法

定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// <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();
}    

一次性作业

1
2
3
4
5
6
7
8
9
10
11
12
/// <summary>
/// 立即执行一次性作业。即发即弃作业仅在创建后立即执行一次。
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("Test_BackgroundJob_Enqueue")]
public async Task<IActionResult> Test_BackgroundJob_Enqueue()
{
    var jobId = BackgroundJob.Enqueue(
            () => Console.WriteLine("执行了立即执行一次性作业"));
    return Ok("执行成功");

延迟作业

1
2
3
4
5
6
7
8
9
10
11
12
13
/// <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("执行成功");
}

延续作业  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// <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的可视化界面,网上倒是很多人扩展了,可以直接用

核心接口

1
2
3
4
5
6
7
Scheduler - 与调度程序交互的主要API。
Job - 你想要调度器执行的任务组件需要实现的接口
JobDetail - 用于定义作业的实例。
Trigger(即触发器) - 定义执行给定作业的计划的组件。
JobBuilder - 用于定义/构建 JobDetail 实例,用于定义作业的实例。
TriggerBuilder - 用于定义/构建触发器实例。
Scheduler 的生命期,从 SchedulerFactory 创建它时开始,到 Scheduler 调用shutdown() 方法时结束;Scheduler 被创建后,可以增加、删除和列举 Job 和 Trigger,以及执行其它与调度相关的操作(如暂停 Trigger)。但是,Scheduler 只有在调用 start() 方法后,才会真正地触发 trigger(即执行 job)

准备工作

1. 引入nuget包

1
Quartz

2.  Program中添加服务注入配置,这里没有集成数据库

1
builder.Services.AddScoped<ISchedulerFactory, StdSchedulerFactory>();

 

3. api中依赖注入

1
2
3
4
5
6
7
8
private readonly ISchedulerFactory _schedulerFactory;
        private readonly IScheduler _scheduler;
        
        public ValuesController( ISchedulerFactory schedulerFactory)
        {
            _schedulerFactory = schedulerFactory;
            _scheduler = _schedulerFactory.GetScheduler().Result;  //通过工场类获得调度器
        }

简单使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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("执行成功");
}

注意,这里的FCBJob是一个集成IJob的类

    public class FCBJob : IJob
    {
        public async Task Execute(IJobExecutionContext context)
        {
            Console.WriteLine("Hello Word!");
            await Task.CompletedTask;
        }
    }

 当job中有依赖注入的服务时,需要自定义JobFactory

复制代码
public class ScopedJobFactory : IJobFactory
{
    private readonly IServiceProvider _serviceProvider;

    public ScopedJobFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
           return _serviceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
    }

    public void ReturnJob(IJob job)
    {
        // 如果你的作业实现了IDisposable,你可以在这里释放它  
        // 但通常,作业的生命周期由Quartz管理,不需要手动释放  
        var disposable = job as IDisposable;
        disposable?.Dispose();
    }
}

    public static class QuartzSchedulerService
    {
        public async static Task ExecQuartzScheuler(this WebApplication app)
        {
            var schedulerFactory=app.Services.GetRequiredService<StdSchedulerFactory>();
            IScheduler _scheduler = await schedulerFactory.GetScheduler();
            _scheduler.JobFactory = new ScopedJobFactory(app.Services);  //重点,这里需要指定我们的JobFactory
            //开启调度器
            await _scheduler.Start();
            //创建触发器(也叫时间策略)
            var trigger = TriggerBuilder.Create()
                            .WithCronSchedule("0/3 * * * * ?")  //使用cron指定运行时间来执行,每3秒运行一次
                            .Build();
            //创建作业实例
            var jobDetail = JobBuilder.Create<ErrorLogJob>()
                            .WithIdentity("Myjob", "fcbgroup")
                            .Build();
            await _scheduler.ScheduleJob(jobDetail, trigger);
        }
    }


    public class ErrorLogJob : IJob
    {
        private readonly CloudDataCenterService _cloudDataCenterService; // 假设有个依赖注入的服务

        // 依赖注入  
        public ErrorLogJob(CloudDataCenterService cloudDataCenterService)
        {
            _cloudDataCenterService = cloudDataCenterService;
        }
        public async Task Execute(IJobExecutionContext context)
        {
            Console.WriteLine("11111");
        }
    }
复制代码

 

 

builder.Services.AddSingleton<StdSchedulerFactory>();
builder.Services.AddSingleton<ErrorLogJob>();
var app = builder.Build();
await app.ExecQuartzScheuler();

 

一个Job执行多个触发器

job和trigger 可以是一对多的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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("执行成功");
}

停止任务调度

1
2
public async Task<IActionResult> ShutdownScheduler()
{  await _scheduler.Shutdown();  return Ok(); <br>}

暂停定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
}

恢复定时任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
}

删除定时任务

1
2
3
4
5
6
7
8
9
public async Task<IActionResult> DeleteJob()
{
    JobKey jobKey = new JobKey("Myjob", "fcbgroup");
    if (await _scheduler.CheckExists(jobKey))
    {
        await _scheduler.DeleteJob(jobKey);
    }
    return Ok();
}

 

posted @   Joni是只狗  阅读(1335)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示