zoukankan      html  css  js  c++  java
  • 在.NET Core 中使用Quartz.NET

    Quartz.NET是功能齐全的开源作业调度系统,可用于最小的应用程序到大型企业系统。

    Quartz.NET具有三个主要概念:

    • job:运行的后台任务
    • trigger:控制后台任务运行的触发器。
    • scheduler:协调job和trigger

    ASP.NET Core通过托管服务对运行“后台任务”具有良好的支持,托管服务在ASP.NET Core应用程序启动时启动,并在应用程序生存期内在后台运行,Quartz.NET版本3.2.0通过Quartz.Extensions.Hosting包引入了对该模式的直接支持,Quartz.Extensions.Hosting可以与ASP.NET Core应用程序一起使用,也可以与基于“通用主机”的工作程序服务一起使用。

    虽然.NET Core可以创建“定时”后台服务(例如,每10分钟运行一次任务),但Quartz.NET提供了更为强大的解决方案, 通过使用Cron表达式,您可以确保任务在特定时间(例如,凌晨2:30)运行,或仅在特定的几天运行,或这些时间的任意组合。Quartz.NET还允许您以集群方式运行应用程序的多个实例,以便在任何时候都只能运行一个实例。

    安装Quartz.NET

    Quartz.NET是一个.NET Standard 2.0 NuGet软件包,所以大部分项目都是支持的,你可以运行安装命令,dotnet add package Quartz.Extensions.Hosting,或者在NNuget可视化安装,如果查看该项目的.csproj,应该是下边这样:

    <Project Sdk="Microsoft.NET.Sdk.Worker">
    
      <PropertyGroup>
        <TargetFramework>net5.0</TargetFramework>
        <UserSecretsId>dotnet-QuartzWorkerService-9D4BFFBE-BE06-4490-AE8B-8AF1466778FD</UserSecretsId>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
        <PackageReference Include="Quartz.Extensions.Hosting" Version="3.2.3" />
      </ItemGroup>
    </Project>
    

    安装完成以后,这个包会自动安装 Quartz.NET包,接下来,我们需要在我们的应用程序中注册Quartz服务和Quartz 。

    添加Quartz.NET hosted service

    修改Program.cs,注册服务

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
    
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    // Add the required Quartz.NET services
                    services.AddQuartz(q =>  
                    {
                        // Use a Scoped container to create jobs. I'll touch on this later
                        q.UseMicrosoftDependencyInjectionScopedJobFactory();
                    });
    
                    // Add the Quartz.NET hosted service
    
                    services.AddQuartzHostedService(
                        q => q.WaitForJobsToComplete = true);
    
                    // other config
                });
    }
    

    UseMicrosoftDependencyInjectionScopedJobFactory(),这个地方告诉Quartz.NET注册一个IJobFactory,然后从DI容器中获取Job,这样也可以使用 Scoped 类型的服务。

    WaitForJobsToComplete():当程序关闭时,此设置可确保Quartz.NET在退出之前等待Job正常结束。

    如果现在运行您的应用程序,您将看到Quartz服务启动,并将有很多日志输出到控制台:

    info: Quartz.Core.SchedulerSignalerImpl[0]
          Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl
    info: Quartz.Core.QuartzScheduler[0]
          Quartz Scheduler v.3.2.3.0 created.
    info: Quartz.Core.QuartzScheduler[0]
          JobFactory set to: Quartz.Simpl.MicrosoftDependencyInjectionJobFactory
    info: Quartz.Simpl.RAMJobStore[0]
          RAMJobStore initialized.
    info: Quartz.Core.QuartzScheduler[0]
          Scheduler meta-data: Quartz Scheduler (v3.2.3.0) 'QuartzScheduler' with instanceId 'NON_CLUSTERED'
      Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally.
      NOT STARTED.
      Currently in standby mode.
      Number of jobs executed: 0
      Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 10 threads.
      Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered.
    
    info: Quartz.Impl.StdSchedulerFactory[0]
          Quartz scheduler 'QuartzScheduler' initialized
    info: Quartz.Impl.StdSchedulerFactory[0]
          Quartz scheduler version: 3.2.3.0
    info: Quartz.Core.QuartzScheduler[0]
          Scheduler QuartzScheduler_$_NON_CLUSTERED started.
    info: Microsoft.Hosting.Lifetime[0]
          Application started. Press Ctrl+C to shut down.
    ...
    

    现在,您已经将Quartz作为托管服务运行在您的应用程序中,但是现在还没有添加需要运行的Job。

    创建一个IJob

    这个地方我创建一个简单的服务,并且我可以从构造函数中获取服务。

    using Microsoft.Extensions.Logging;
    using Quartz;
    using System.Threading.Tasks;
    
    [DisallowConcurrentExecution]
    public class HelloWorldJob : IJob
    {
        private readonly ILogger<HelloWorldJob> _logger;
        public HelloWorldJob(ILogger<HelloWorldJob> logger)
        {
            _logger = logger;
        }
    
        public Task Execute(IJobExecutionContext context)
        {
            _logger.LogInformation("Hello world!");
            return Task.CompletedTask;
        }
    }
    

    我还用[DisallowConcurrentExecution]特性,防止Quartz.NET尝试同时运行同一个作业。

    设置Job

    这个地方通常使用Cron表达式,来设置job的执行时间。

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddQuartz(q =>  
                {
                    q.UseMicrosoftDependencyInjectionScopedJobFactory();
    
                    // Create a "key" for the job
                    var jobKey = new JobKey("HelloWorldJob");
    
                    // Register the job with the DI container
                    q.AddJob<HelloWorldJob>(opts => opts.WithIdentity(jobKey));
    
                    // Create a trigger for the job
                    q.AddTrigger(opts => opts
                        .ForJob(jobKey) // link to the HelloWorldJob
                        .WithIdentity("HelloWorldJob-trigger") // give the trigger a unique name
                        .WithCronSchedule("0/5 * * * * ?")); // run every 5 seconds
    
                });
                services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
                // ...
            });
    

    现在运行应用程序,您将看到和以前相同的启动消息,然后每隔5秒钟就会看到HelloWorldJob写入控制台的信息:

    将配置提取到appsettings.json

    一般情况,我们都不会把cron表达式写死在代码中,一般是设置在appsettings.json中

    {
      "Quartz": {
        "HelloWorldJob": "0/5 * * * * ?"
      }
    }
    

    为了更简单的注册服务,这个地方我简单做了一个封装,这样也更灵活。

    public static class ServiceCollectionQuartzConfiguratorExtensions
    {
        public static void AddJobAndTrigger<T>(
            this IServiceCollectionQuartzConfigurator quartz,
            IConfiguration config)
            where T : IJob
        {
            // Use the name of the IJob as the appsettings.json key
            string jobName = typeof(T).Name;
    
            // Try and load the schedule from configuration
            var configKey = $"Quartz:{jobName}";
            var cronSchedule = config[configKey];
    
            // Some minor validation
            if (string.IsNullOrEmpty(cronSchedule))
            {
                throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {configKey}");
            }
    
            // register the job as before
            var jobKey = new JobKey(jobName);
            quartz.AddJob<T>(opts => opts.WithIdentity(jobKey));
    
            quartz.AddTrigger(opts => opts
                .ForJob(jobKey)
                .WithIdentity(jobName + "-trigger")
                .WithCronSchedule(cronSchedule)); // use the schedule from configuration
        }
    }
    

    然后修改Program.cs,然后使用扩展方法:

    public class Program
    {
       public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
    
       public static IHostBuilder CreateHostBuilder(string[] args) =>
           Host.CreateDefaultBuilder(args)
               .ConfigureServices((hostContext, services) =>
               {
                   services.AddQuartz(q =>
                   {
                       q.UseMicrosoftDependencyInjectionScopedJobFactory();
    
                       // Register the job, loading the schedule from configuration
                       q.AddJobAndTrigger<HelloWorldJob>(hostContext.Configuration);
                   });
    
                   services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
               });
    }
    

    再次运行该应用程序将提供相同的输出:Job每5秒输入一次信息。

    原文作者: andrewlock
    原文链接: https://andrewlock.net/using-quartz-net-with-asp-net-core-and-worker-services/

    最后

    欢迎扫码关注我们的公众号 【全球技术精选】,专注国外优秀博客的翻译和开源项目分享,也可以添加QQ群 897216102

  • 相关阅读:
    linux内核中GNU C和标准C的区别
    linux内核中GNU C和标准C的区别
    Getting start with dbus in systemd (02)
    Getting start with dbus in systemd (01)
    Getting start with dbus in systemd (03)
    物理内存相关的三个数据结构
    数据类型对应字节数(32位,64位 int 占字节数)
    Linux kernel 内存
    共模电感的原理以及使用情况
    [原创]DC-DC输出端加电压会烧毁
  • 原文地址:https://www.cnblogs.com/myshowtime/p/14319212.html
Copyright © 2011-2022 走看看