Net6 定時調度Quartz.AspNetCore(3.5.0)的使用

十年河東,十年河西,莫欺少年窮

學無止境,精益求精

1、概述

Quartz.Net是根據Java的Quartz用C#改寫而來,Quartz.NET是一個開源的作業調度框架,非常適合在平時的工作中,定時輪詢數據庫同步,定時郵件通知,定時處理數據等。 Quartz.NET允許開發人員根據時間間隔來調度作業。它有很多特徵如:數據庫支持,集羣,插件,支持cron-like表達式等等。

2、參考

Quartz.Net源碼:https://github.com/quartznet/quartznet

官方學習文檔:http://www.quartz-scheduler.net/documentation/index.html

3、Quartz.Net說明

Quartz主要有三部分組成任務(Job)、觸發器(Trigger)和調度器(Schedule)。

3.1、作業

Job就是執行的作業,Job需要繼承IJob接口,實現Execute方法。Job中執行的參數從Execute方法的參數中獲取。

3.2、觸發器

觸發器常用的有兩種:SimpleTrigger觸發器和CronTrigger觸發器。

SimpleTrigger

 實現簡單業務,如每隔幾分鐘,幾小時觸發執行,並限制執行次數。

  • 重複執行:WithRepeatCount()/RepeatForever()
  • 設置間隔時間:WithInterval()
  • 定時執行:StartAt()/StartNow()
  • 設定優先級:WithPriority(),默認爲5
var trigger = TriggerBuilder.Create()
                       .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).WithRepeatCount(5))//間隔2秒 執行6次
                       .UsingJobData("key1", 321)
                       .WithIdentity("trigger", "group")
                       .Build();

CronTrigger

CornTrigger需要接合Corn表達式

常用cron表達式

  • */10 * * * * ? 每隔10秒執行一次
  • 0 */5 * * * ? 每隔5分鐘執行一次
  • 0 2,22,32 * * * ? 在2分、22分、32分執行一次
  • 0 0 4-8 * * ? 每天4-8點整點執行一次
  • 0 0 2 * * ? 每天凌晨2點執行一次
  • 0 0 2 1 * ? 每月1號凌晨2點執行一次

cron表達式生成器

現在也有許多在線的cron表達式生成器:

            var trigger = TriggerBuilder.Create().StartNow()
                            .WithCronSchedule("0/2 * * * * ? *")//每兩秒執行一次
                            .Build();

3.3、調度器

調度器就是將任務和觸發器綁定,讓觸發器觸發的時候去執行任務。

3.4、參數說明

  • SetJobData:設置JobData

  • StoreDurably:孤立存儲,指即使該JobDetail沒有關聯的Trigger,也會進行存儲

  • RequestRecovery:請求恢復,指應用崩潰後再次啓動,會重新執行該作業

  • WithIdentity:作業的唯一標識

  • WithDescription:作業的描述信息

除此之外,Quartz.Net還支持兩個非常有用的特性:

  • DisallowConcurrentExecution:禁止並行執行,該特性是針對JobDetail生效的

  • PersistJobDataAfterExecution:在執行完成後持久化JobData,該特性是針對Job類型生效的,意味着所有使用該Job的JobDetail都會在執行完成後持久化JobData。

4、新建控制檯應用程序

項目清單如下

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
    <PackageReference Include="Quartz.AspNetCore" Version="3.5.0" />
      <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
      <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
      <PackageReference Include="NLog.Extensions.Logging" Version="5.0.4" />
  </ItemGroup>

  <ItemGroup>
    <None Update="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>

</Project>
View Code

4.1、簡單時間間隔作業

using Newtonsoft.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog.Extensions.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using System;

namespace BatteryService // Note: actual namespace depends on the project name.
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            ConfigurationBuilder builder = new ConfigurationBuilder();
            builder.AddJsonFile("appsettings.json", true, true);
            var ConfigRoot = builder.Build();//根節點
            IServiceCollection Services = new ServiceCollection(); 
            // 
            Services.AddLogging(log => { log.AddConsole(); log.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information); }); 
            Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();//註冊ISchedulerFactory的實例。
            Services.AddScoped<QuartzService>();//註冊 QuartzService 的實例。
            using (ServiceProvider provider = Services.BuildServiceProvider())
            {
                var service = provider.GetService<QuartzService>();
                await service.with2Seconds();
            };
            CreateHostBuilder(args).Run();
        }

        public static IHost CreateHostBuilder(string[] args)
        {
            var builder = Host.CreateDefaultBuilder(args)
                 .ConfigureServices((hostContext, services) =>
                 {
                     //services.AddHostedService<IotService>();
                 }).UseWindowsService();
            var host = builder.Build();

            return host;
        }
    }

    public class QuartzService
    { 
        private readonly ISchedulerFactory _schedulerFactory; 
        private IScheduler _scheduler;
        private readonly ILogger<QuartzService> logger;
        public QuartzService(ISchedulerFactory schedulerFactory, ILogger<QuartzService> logger)
        {
            _schedulerFactory = schedulerFactory;
            this.logger = logger;
        }

        public async Task with2Seconds()
        {
          
            //通過調度工廠獲得調度器
            _scheduler = await _schedulerFactory.GetScheduler(); 
            //開啓調度器
            await _scheduler.Start();
            logger.LogInformation("調度器開始工作");
            //創建一個觸發器
            var trigger = TriggerBuilder.Create().StartNow()
                            .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever())//每兩秒執行一次
                            .Build();
            //創建任務
            var jobDetail = JobBuilder.Create<MyJob>()
                            .WithIdentity("job1", "group")
                            .Build();
            //將觸發器和任務器綁定到調度器中
            await _scheduler.ScheduleJob(jobDetail, trigger);
        }
    }
    [DisallowConcurrentExecution]//禁止並行執行,該特性是針對JobDetail生效的
    public class MyJob : IJob
    {  
        public Task Execute(IJobExecutionContext context)
        {
            return Task.Run(() =>
            {
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); 
            });
        }
    } 
}  
View Code

 4.2、可以傳遞參數的簡單作業(使用了特性【DisallowConcurrentExecution】防併發作業)

using Newtonsoft.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog.Extensions.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using System;

namespace BatteryService // Note: actual namespace depends on the project name.
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            ConfigurationBuilder builder = new ConfigurationBuilder();
            builder.AddJsonFile("appsettings.json", true, true);
            var ConfigRoot = builder.Build();//根節點
            IServiceCollection Services = new ServiceCollection(); 
            // 
            Services.AddLogging(log => { log.AddConsole(); log.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information); }); 
            Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();//註冊ISchedulerFactory的實例。
            Services.AddScoped<QuartzService>();//註冊 QuartzService 的實例。
            using (ServiceProvider provider = Services.BuildServiceProvider())
            {
                var service = provider.GetService<QuartzService>();
                await service.with2Seconds();
            };
            CreateHostBuilder(args).Run();
        }

        public static IHost CreateHostBuilder(string[] args)
        {
            var builder = Host.CreateDefaultBuilder(args)
                 .ConfigureServices((hostContext, services) =>
                 {
                     //services.AddHostedService<IotService>();
                 }).UseWindowsService();
            var host = builder.Build();

            return host;
        }
    }

    public class QuartzService
    { 
        private readonly ISchedulerFactory _schedulerFactory; 
        private IScheduler _scheduler;
        private readonly ILogger<QuartzService> logger;
        public QuartzService(ISchedulerFactory schedulerFactory, ILogger<QuartzService> logger)
        {
            _schedulerFactory = schedulerFactory;
            this.logger = logger;
        }

        public async Task with2Seconds()
        {
          
            //通過調度工廠獲得調度器
            _scheduler = await _schedulerFactory.GetScheduler(); 
            //開啓調度器
            await _scheduler.Start();
            logger.LogInformation("調度器開始工作");
            //創建一個觸發器
            var trigger = TriggerBuilder.Create().StartNow().UsingJobData("Trigger", "我是在Trigger中設置的參數").UsingJobData("TriggerParmCount", 1)
                            .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).WithRepeatCount(1))//每兩秒執行一次
                            .Build();
            //創建任務
            var jobDetail = JobBuilder.Create<MyJob>()
                .UsingJobData("Job_1", "我是在Job中設置的參數")
                .UsingJobData("Job_2", "我是在Job中設置的參數")
                .UsingJobData("ParmCount", 2)
                .WithIdentity("jobName", "group")
                .Build();
            //將觸發器和任務器綁定到調度器中
            await _scheduler.ScheduleJob(jobDetail, trigger);
        }
    }
    [DisallowConcurrentExecution]//禁止並行執行,該特性是針對JobDetail生效的
    public class MyJob : IJob
    {  
        public Task Execute(IJobExecutionContext context)
        {
            var jobData = context.JobDetail.JobDataMap;//獲取Job中的參數 
            var triggerData = context.Trigger.JobDataMap;//獲取Trigger中的參數
            var allData = context.MergedJobDataMap;//獲取Job和Trigger中合併的參數
            var Trigger = triggerData.GetString("Trigger");
            int TriggerParmCount = triggerData.GetInt("TriggerParmCount");

            var Job_1 = triggerData.GetString("Job_1");
            var Job_2 = triggerData.GetString("Job_2");
            int ParmCount = triggerData.GetInt("ParmCount");

            return Task.Run(() =>
            {
                foreach (var item in jobData)
                {
                    Console.WriteLine($"Job中的參數,鍵爲:{item.Key}值爲:{item.Value}");
                }
                Console.WriteLine("-----------------------------------------------");
                foreach (var item in triggerData)
                {
                    Console.WriteLine($"Trigger中的參數,鍵爲:{item.Key}值爲:{item.Value}");
                }
                Console.WriteLine("-----------------------------------------------");
                foreach (var item in allData)
                {
                    Console.WriteLine($"Job和Trigger中的所有參數,鍵爲:{item.Key}值爲:{item.Value}");
                }
            });
        }
    } 
}  
View Code

 4.3、可變參數簡單作業(使用了特性【PersistJobDataAfterExecution】存儲JobDataMap副本)

using Newtonsoft.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog.Extensions.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
using System;

namespace BatteryService // Note: actual namespace depends on the project name.
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            ConfigurationBuilder builder = new ConfigurationBuilder();
            builder.AddJsonFile("appsettings.json", true, true);
            var ConfigRoot = builder.Build();//根節點
            IServiceCollection Services = new ServiceCollection(); 
            // 
            Services.AddLogging(log => { log.AddConsole(); log.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information); }); 
            Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();//註冊ISchedulerFactory的實例。
            Services.AddScoped<QuartzService>();//註冊 QuartzService 的實例。
            using (ServiceProvider provider = Services.BuildServiceProvider())
            {
                var service = provider.GetService<QuartzService>();
                await service.with2Seconds();
            };
            CreateHostBuilder(args).Run();
        }

        public static IHost CreateHostBuilder(string[] args)
        {
            var builder = Host.CreateDefaultBuilder(args)
                 .ConfigureServices((hostContext, services) =>
                 {
                     //services.AddHostedService<IotService>();
                 }).UseWindowsService();
            var host = builder.Build();

            return host;
        }
    }

    public class QuartzService
    { 
        private readonly ISchedulerFactory _schedulerFactory; 
        private IScheduler _scheduler;
        private readonly ILogger<QuartzService> logger;
        public QuartzService(ISchedulerFactory schedulerFactory, ILogger<QuartzService> logger)
        {
            _schedulerFactory = schedulerFactory;
            this.logger = logger;
        }

        public async Task with2Seconds()
        {
          
            //通過調度工廠獲得調度器
            _scheduler = await _schedulerFactory.GetScheduler(); 
            //開啓調度器
            await _scheduler.Start();
            logger.LogInformation("調度器開始工作");
            //創建一個觸發器
            var trigger = TriggerBuilder.Create().StartNow()
                .UsingJobData("name", "陳臥龍")
                .UsingJobData("sex", "")
                .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).WithRepeatCount(9))//每兩秒執行一次 共執行10次
                .Build();
            //創建任務
            var jobDetail = JobBuilder.Create<MyJob>()
                .UsingJobData("age", 28)
                .UsingJobData("weight", 70) 
                .WithIdentity("jobName", "group")
                .Build();
            //將觸發器和任務器綁定到調度器中
            await _scheduler.ScheduleJob(jobDetail, trigger);
        }
    }
    [PersistJobDataAfterExecution]// 更新JobDetail的JobDataMap的存儲副本,以便下一次執行這個任務接收更新的值而不是原始存儲的值
    public class MyJob : IJob
    {  
        public Task Execute(IJobExecutionContext context)
        { 
            var allData = context.MergedJobDataMap;//獲取Job和Trigger中合併的參數
            var name = allData.GetString("name");
            var sex = allData.GetString("sex"); 
            var age = allData.GetInt("age");
            var weight = allData.GetInt("weight");
            Console.WriteLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            Console.WriteLine($"原始參數爲,姓名:{name},性別:{sex},年齡:{age},體重:{weight}");

            allData["name"] = name + "_" + new Random().Next(1, 10);
            allData["sex"] = sex + "_" + new Random().Next(1, 10);
            allData["age"] = new Random().Next(20, 100);
            allData["weight"] = new Random().Next(60, 100);
            return Task.Run(() =>
            {  
                foreach (var item in allData)
                {
                    Console.WriteLine($"改變後的參數,鍵爲:{item.Key}值爲:{item.Value}");
                }
                Console.WriteLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            });
        }
    } 
}  
View Code

 4.4、Corn表達式作業調度

corn表示式和簡單調度的區別是:一個使用固定時間間隔作業,比較簡單。一個可以通過corn表達式進行復雜的時間場景調度

現在也有許多在線的cron表達式生成器:

 

每月最後一天23:59分執行

日部分截圖

 

 

 小時部分截圖

 

 分鐘部分截圖

 

 秒部分截圖

 

 

 最終生成的Corn 表達式爲:0 59 23 L * ? *

示例代碼如下:

        public async Task with2Seconds()
        {
          
            //通過調度工廠獲得調度器
            _scheduler = await _schedulerFactory.GetScheduler(); 
            //開啓調度器
            await _scheduler.Start();
            logger.LogInformation("調度器開始工作");
            //創建一個觸發器
            var trigger = TriggerBuilder.Create().StartNow().WithCronSchedule("0 59 23 L * ? *") //每月月底23.59分執行
                .UsingJobData("name", "陳臥龍")
                .UsingJobData("sex", "")  
                .Build();
            //創建任務
            var jobDetail = JobBuilder.Create<MyJob>()
                .UsingJobData("age", 28)
                .UsingJobData("weight", 70) 
                .WithIdentity("jobName", "group")
                .Build();
            //將觸發器和任務器綁定到調度器中
            await _scheduler.ScheduleJob(jobDetail, trigger);
        }
View Code

核心配置:

  var trigger = TriggerBuilder.Create().StartNow().WithCronSchedule("0 59 23 L * ? *") //每月月底23.59分執行
                .UsingJobData("name", "陳臥龍")
                .UsingJobData("sex", "")  
                .Build();

@天才臥龍的博科人、

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章