ASP.NET Core托管运行Quartz.NET做业调度详解

Quartz.NET这么NB的做业调度系统,不会还行?html

今天介绍一下Quartz.NET的托管运行,官网传送门git

1、前言

Quartz.NET,按官网上的说法,是一款功能齐全的任务调度系统,从小型应用到大型企业级系统都能适用。在众多项目中,Quartz.NET以可靠、集群的方式,被用做在定时器上运行后台任务的一种方式。github

Quartz.NET主要完成两个方面的内容:c#

  1. 基于时间计划的后台做业;
  2. 基于因时间计划的触发的任务运行。

ASP.NET Core自己对于经过托管服务运行后台任务就支持的很好。当ASP.NET启动托管服务时,应用程序启动,并在生命周期内在后台运行。经过建立Quartz.NET托管服务,可使用标准的.Net Core托管服务,在后台运行任务。安全

Quartz.NET能够建立定时的任务,例如每十分钟运行一个任务。除此以外,Quartz.NET还能够经过Cron触发器,定义任务在特定的日子或特定的时间运行,例如天天凌晨两点执行一个任务。它还容许以集群的方式运行应用程序的多个实例,以便在任什么时候间确保只有一个实例运行给定的任务。bash

下面,就针对这些特性和功能,进行详细的说明。微信

    为防止非受权转发,这儿给出本文的原文连接:http://www.javashuo.com/article/p-uqtgnnvo-nv.htmlapp

2、安装Quartz.NET

Quartz.NET提供了NuGet包,因此安装很简单:框架

% dotnet add package quartz

这是个司机就知道,不详说了。async

看一下安装后的.csproj文件内容:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="quartz" Version="3.2.2" />
  </ItemGroup>
</Project>

3、经过IJob建立任务类

咱们用个例子来讲明 - 建立一个Demo的实现。它将写入ILogger<>。咱们会使用Quartz.NET的接口IJob来实现,并使用依赖注入将日志注入到构造函数中。

[DisallowConcurrentExecution]
public class DemoJob : IJob
{
    private readonly ILogger<DemoJob> _logger;
    public DemoJob(ILogger<DemoJob> logger)
    
{
        _logger = logger;
    }

    public Task Execute(IJobExecutionContext context)
    
{
        _logger.LogInformation("Demo !");
        return Task.CompletedTask;
    }
}

在类的前面,我用了一个DisallowConcurrentExecution属性。这个属性能够防止Quartz.NET同时运行相同的做业。

4、经过IJobFactory建立任务工厂

一般状况下,Quartz.NET会使用Activator.CreateInstance来建立做业的实例。

在咱们这个例子里,咱们换一种方式。使用IJobFactory实现,并与ASP.NET Core的依赖注入容器挂钩。

public class SingletonJobFactory : IJobFactory
{
    private readonly IServiceProvider _serviceProvider;
    public SingletonJobFactory(IServiceProvider serviceProvider)
    
{
        _serviceProvider = serviceProvider;
    }

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

    public void ReturnJob(IJob job)
    
{
    }
}

这个IJobFactory的实现,在构造函数中引入IServiceProvider,并实现接口。

接口中,最重要的是NewJob()方法。这个方法须要返回Quartz.NET调度器请求的IJob。在咱们的例子中,咱们直接委托给IServiceProvider,并让DI容器找到所需的实例。

ReturnJob()方法是调度程序返回和销毁IJobFactory建立的做业的地方。不过,由于咱们使用了IServiceProvider,而它没有提供这样的处理。因此,从安全的角度,应该使用单例做业。

5、配置做业

在第三节中,咱们建立了一个IJob的实现。这个实现直接使用就能够。

可是,咱们这儿要加点内容。咱们把Quartz的托管服务作成一个通用实现,来调度任意的做业。所以,咱们建立一个简单的DTO,并使用它来定义一个给定做业类型的时间器调度:

public class JobSchedule
{

    public JobSchedule(Type jobType, string cronExpression)
    
{
        JobType = jobType;
        CronExpression = cronExpression;
    }

    public Type JobType { get; }
    public string CronExpression { get; }
}

在这个类中,JobType是一个做业的类型,例如本例子中的DemoJobCronExpression是一个Cron的表达式。

Cron表达式容许复杂的计时器调度,因此能够设置规则,好比每月的5号和20号,在早上8点到10点之间每半小时触发一次。

关于Quartz.NETCron表达式,能够在这儿查到。

注意:不一样操做系统使用的Cron表达式有必定的区别,写表达式的时候必定要注意这一点。

而后,咱们把做业添加到Startup.ConfigureServices()中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddSingleton<IJobFactory, SingletonJobFactory>();
    services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();

    services.AddSingleton<DemoJob>();
    services.AddSingleton(new JobSchedule(
        jobType: typeof(DemoJob),
        cronExpression: "0/5 * * * * ?")); // 每5秒运行一次
}

这段代码向DI添加了四个单例对象:

  1. SingletonJobFactory,第四节的类,用于建立做业实例;
  2. ISchedulerFactory的一个实现,是内置的StdSchedulerFactory,用于处理调度和管理做业;
  3. DemoJob做业自己;
  4. DemoJob的一个JobSchedule实例,该实例具备每5秒运行一次的Cron表达式。

如今,主要代码已经完成,就差Quartz托管服务了。

6、建立Quartz托管服务

QuartzHostedService是本身建立的Quartz托管服务,继承于IHostedService,用于设置Quartz调度器,并在后台启动它。

先看一下完整的代码:

public class QuartzHostedService : IHostedService
{
    private readonly ISchedulerFactory _schedulerFactory;
    private readonly IJobFactory _jobFactory;
    private readonly IEnumerable<JobSchedule> _jobSchedules;

    public QuartzHostedService(ISchedulerFactory schedulerFactory, IJobFactory jobFactory, IEnumerable<JobSchedule> jobSchedules)
    
{
        _schedulerFactory = schedulerFactory;
        _jobSchedules = jobSchedules;
        _jobFactory = jobFactory;
    }

    public IScheduler Scheduler { get; set; }

    public async Task StartAsync(CancellationToken cancellationToken)
    
{
        Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
        Scheduler.JobFactory = _jobFactory; 

        foreach (var jobSchedule in _jobSchedules)
        {
            var job = CreateJob(jobSchedule);
            var trigger = CreateTrigger(jobSchedule);

            await Scheduler.ScheduleJob(job, trigger, cancellationToken);
        }

        await Scheduler.Start(cancellationToken);
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    
{
        await Scheduler?.Shutdown(cancellationToken);
    }

    private ITrigger CreateTrigger(JobSchedule schedule)
    
{
        return TriggerBuilder
        .Create()
        .WithIdentity($"{schedule.JobType.FullName}.trigger")
        .WithCronSchedule(schedule.CronExpression)
        .WithDescription(schedule.CronExpression)
        .Build();
    }

    private IJobDetail CreateJob(JobSchedule schedule)
    
{
        var jobType = schedule.JobType;
        return JobBuilder
            .Create(jobType)
            .WithIdentity(jobType.FullName)
            .WithDescription(jobType.Name)
            .Build();
    }
}

解释一下这段代码:

这段代码中,QuartzHostedService有三个依赖项:Startup.ConfigureServices()中注入的ISchedulerFactoryIJobFactory,以及一个IEnumerable。在第五节的代码中,咱们只向DI添加了一个JobSchedule,就是DemoJob。咱们也能够添加多个JobSchedule,他们都会在这个IEnumerable中被注入到托管服务中。

StartAsync在应用程序启动时被调用,它是咱们配置Quartz的地方。咱们首先建立IScheduler的一个实例,为它分配一个属性供之后使用,并将调度程序的JobFactory设置为注入的实例:

public async Task StartAsync(CancellationToken cancellationToken)
{
    Scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
    Scheduler.JobFactory = _jobFactory; 
    //... 
}

而后,循环注入的做业调度,并在类的最后使用CreateJobCreateTrigger方法为每一个做业建立一个IJobDetailITrigger。实际应用中若是有别的须要,也能够经过扩展JobSchedule DTO来定制它。

最后,在调度了全部做业以后,调用Scheduler.Start()来实际在后台启动Quartz.NET调度器。当应用程序关闭时,框架将调用StopAsync(),此时能够调用Scheduler.Shutdown()来安全地关闭调度程序进程。

所有完成后,咱们启动QuartzHostedService

public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddHostedService<QuartzHostedService>();
}

运行程序,能够看到结果:

demo.DemoJob: Information: Demo !
info: demo.DemoJob[0]
      Demo !
demo.DemoJob: Information: Demo !
info: demo.DemoJob[0]
      Demo !
demo.DemoJob: Information: Demo !
info: demo.DemoJob[0]
      Demo !
demo.DemoJob: Information: Demo !
info: demo.DemoJob[0]
      Demo !

本文的代码,在https://github.com/humornif/Demo-Code/tree/master/0029/demo

 


 

微信公众号:老王Plus

扫描二维码,关注我的公众号,能够第一时间获得最新的我的文章和内容推送

本文版权归做者全部,转载请保留此声明和原文连接

相关文章
相关标签/搜索