Quartz NetCore定时器任务应用之基于Quartz

Quartz Net 是一个强大,开源,轻量的作业调度框架,可以创建简单或复杂的作业调度来执行一个Task。

Quartz主要由3部分组成:

Scheduler:调度器,根据Trigger中设置调用周期执行Job。

Trigger:触发器,设置Job执行周期。

Job:具体需要执行的业务。

本篇介绍Quartz在netcore中的一种应用纯Quartz类库开发netcore定时器任务,下篇会介绍通过Quartz.Extensions.Hosting类库更简便的开发基于netcore的定时器任务

1、Quartz,通过Manage NuGet Pakage / Pakage Manage Console 安装Quartz,当前最新版本3.6.2

 2、创建2个任务(IJob)ServiceJobA,ServiceJobB分别设置DisallowConcurrentExecution attribute,阻止并发调用

[DisallowConcurrentExecution]
public class ServiceJobA : IJob
{
    private readonly ILogger<ServiceJobA> _logger;

    public ServiceJobA(ILogger<ServiceJobA> logger)
    {
        _logger = logger;
    }   

    public Task Execute(IJobExecutionContext context)
    {
        Task.Run(() => 
        {
            _logger.LogInformation($"DateTime.Now: {DateTime.Now.ToLongTimeString()}, The service job A has been excuted.");
        });
        return Task.CompletedTask;
    }
}
ServiceJobA
using Quartz;

namespace API.Quartz.QuartzV2;

[DisallowConcurrentExecution]
public class ServiceJobB : IJob
{
    private readonly ILogger<ServiceJobA> _logger;

    public ServiceJobB(ILogger<ServiceJobA> logger)
    {
        _logger = logger;
    }

    public Task Execute(IJobExecutionContext context)
    {
        Task.Run(() =>
        {
            _logger.LogInformation($"DateTime.Now: {DateTime.Now.ToLongTimeString()}, The service job B has been excuted.");
        });
        return Task.CompletedTask;
    }
}
ServiceJobB

3、创建QuartzJobFactory,目的是通过ServiceProvider从容器中获取IJob,可以参考Quartz源码中Quartz.Simpl.SimpleJobFactory,创建自定义JobFactory之后就可以给调度器(Scheduler)指定自定义的JobFactory。

using Quartz;
using Quartz.Spi;

namespace API.Quartz.QuartzV2
{
    public class QuartzJobFactory : IJobFactory
    {
        private readonly IServiceProvider _serviceProvider;
        public QuartzJobFactory(IServiceProvider serviceProvider)
        { 
            _serviceProvider = serviceProvider;
        }

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

        /// <summary>
        /// Allows the job factory to destory/cleanup the job if needed
        /// </summary>
        /// <param name="job"></param>
        public void ReturnJob(IJob job)
        {
            var disposable = job as IDisposable;
            disposable?.Dispose();
        }
    }
}
JobFactory

通过ServcieProvider从IOC容器获取IJob,IJob的实例就需要添加到IOC容器中,从而在IJob的实例可以通过构造函数的方式实现依赖注入,比如缓存类,日志类,及第三方或自定义的service服务。

 

 4、创建HostService,轮询执行IJob

using Quartz;
using Quartz.Spi;

namespace API.Quartz.QuartzV2
{
    public class QuartzHostedService : IHostedService
    {
        private readonly ISchedulerFactory _schedulerFactory;
        private readonly IJobFactory _jobFactory;
        private readonly List<JobScheduler> _jobSchedulers;

        public QuartzHostedService(ISchedulerFactory schedulerFactory, 
            IJobFactory jobFactory,
            List<JobScheduler> jobSchedulers)
        {
            _schedulerFactory = schedulerFactory ?? throw new ArgumentNullException(nameof(schedulerFactory));
            _jobFactory = jobFactory ?? throw new ArgumentNullException(nameof(jobFactory));
            _jobSchedulers = jobSchedulers ?? throw new ArgumentNullException(nameof(jobSchedulers));
        }

        private IScheduler _scheduler { get; set; }

        public async Task StartAsync(CancellationToken cancellationToken)
        {
            _scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
            _scheduler.JobFactory = _jobFactory;
            foreach (var jobScheduler in _jobSchedulers)
            {
                var job = CreateJob(jobScheduler);
                var trigger = CreateTrigger(jobScheduler);
                await _scheduler.ScheduleJob(job, trigger, cancellationToken);
            }
            await _scheduler.Start(cancellationToken);
        }

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

        private static IJobDetail CreateJob(JobScheduler schedule)
            => JobBuilder.Create(schedule.JobType).WithIdentity(schedule.JobType.Name).WithDescription(schedule.JobType.FullName).Build();

        private static ITrigger CreateTrigger(JobScheduler schedule)
            => TriggerBuilder.Create().WithIdentity($"{schedule.JobType.Name}.trigger").WithCronSchedule(schedule.CronString).WithDescription(schedule.CronString).Build();
    }
}
HostService

 

_scheduler = await _schedulerFactory.GetScheduler(cancellationToken);

这里之所以能用构造函数依赖注入的_schedulerFactory是因为在IOC容器中注入了StdSchedulerFactory

  builder.Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();

  获取到调度器(Scheduler)之后,给Scheduler指定JobFactory,及以上创建的QuartzJobFactory

  _scheduler.JobFactory = _jobFactory;

5、为了调用方便,这里又定义了一个Job和Trigger的关系类JobTrigger,指定JobType及Cron表达式

namespace API.Quartz.QuartzV2;
public class JobTrigger
{
    public Type JobType { get; private set; }
    public String CronString { get; private set; }
    public JobTrigger(Type jobType, string cronString)
    {
        JobType = jobType;
        CronString = cronString;
    }
}
JobTrigger

6、在IOC容器中注入StdSchedulerFactory, ServiceJobA,ServiceJobB及初始化一系列JobTrigger任务串,最后向IOC中注入了自定义的QuartzHostedService

var config = builder.Configuration;
builder.Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
builder.Services.AddTransient<API.Quartz.QuartzV2.ServiceJobA>();
builder.Services.AddTransient<API.Quartz.QuartzV2.ServiceJobB>();
builder.Services.AddSingleton<IJobFactory, QuartzJobFactory>();
builder.Services.AddSingleton(sp
    => new List<JobTrigger>
    {
        new JobTrigger(typeof(API.Quartz.QuartzV2.ServiceJobA), config[$"Quartz:{typeof(API.Quartz.QuartzV2.ServiceJobA).Name}"]),
        new JobTrigger(typeof(API.Quartz.QuartzV2.ServiceJobB), config[$"Quartz:{typeof(API.Quartz.QuartzV2.ServiceJobB).Name}"])
    });
builder.Services.AddHostedService<QuartzHostedService>();
IOC

7、appsettings.json 配置了每个IJob 调用的周期Cron

  "Quartz": {
    "ServiceJobA": "0/5 * * * * ?",
    "ServiceJobB": "0/30 * * * * ?"
  }

工程文件目录

 

启动程序,效果如下:

 

 JobServiceA每5s执行一次,JobServiceB每30s执行一次。

posted @ 2023-03-04 09:35  云霄宇霁  阅读(701)  评论(0编辑  收藏  举报