• 任务调度之Quartz.Net基础


      最近公司要求将之前的系统设计文档补上,于是大家就都被分配了不同的任务,紧锣密鼓的写起了文档来。发现之前项目中使用了Quartz.Net来做一些定时任务的调度,比如定时的删除未支付的订单,定时检查支付状态是否回调成功等业务。现在看起来对代码居然有点陌生了,于是决定写篇博文来记录下Quartz.Net 的基本使用。

      这里以Quartz.Net 3.0+为例,Quartz.Net中最核心三大对象分别为:

    1. IScheduler: 单元/实例,通过这里完成定时任务的配置,只有单元启动后里面的作业才能正常运行
    2. IJob:定时执行的作业就是Job
    3. ITrigger:定时策略 

      我们先来看一下最简单的使用吧,以下代码创建一个Scheduler并启动之。

    StdSchedulerFactory factory = new StdSchedulerFactory();
    IScheduler scheduler = await factory.GetScheduler();
    await scheduler.Start();

      然后我们创建一个继承自IJob的TestJob类,其主要实现IJob的Execute方法,该方法则为当该任务被调度执行的时候所执行的方法。

        public class TestJob : IJob
        {
            public TestJob()
            {
                Console.WriteLine("This is TestJob的构造函数。。。");
            }
    
            public async Task Execute(IJobExecutionContext context)
            {
                await Task.Run(() =>
                {
                    Console.WriteLine($"This is {Thread.CurrentThread.ManagedThreadId} {DateTime.Now}");
                });
            }
        }

      接下来使用JobBuilder创建我们的TestJob

    IJobDetail jobDetail = JobBuilder.Create<TestJob>()
        .WithIdentity("testjob", "group")
        .WithDescription("This is TestJob")
        .Build();

       创建完Job后需要添加一个trigger,其中StartAt可以指定在隔多少时间后开始触发作业,StartNow则是马上开始,WithCronSchedule是使用Cron表达式来标识时间,关于Cron表达式可以参考另一篇博文

    ITrigger trigger = TriggerBuilder.Create()
        .WithIdentity("testtrigger1", "group")
        .StartAt(new DateTimeOffset(DateTime.Now.AddSeconds(10)))
        //.StartNow()//StartAt
        .WithCronSchedule("0/10 * * * * ?")//每隔10秒钟
        .WithDescription("This is testjob's Trigger")
        .Build();

      创建完Job和trigger后,最后需要将其添加到Scheduler中

    await scheduler.ScheduleJob(jobDetail, trigger);

      这样最简单的作业调度就完成了。但是在实际工作中,有时候我们需要传递参数到Job中,这该怎么做呢?Quartz.Net为我们提供了两种方法:

    1. 通过在创建Job的时候在其JobDataMap中添加参数,然后在Job的Execute方法中通过context.JobDetail.JobDataMap来获取
    jobDetail.JobDataMap.Add("param", "value1");
    JobDataMap dataMap = context.JobDetail.JobDataMap;
    dataMap.Get("param")

       2. 通过在trigger中通过trigger.JobDataMap.Add添加参数,在Job中通过context.Trigger.JobDataMap获取

    trigger.JobDataMap.Add("param", "value2");
    JobDataMap dataMap = context.Trigger.JobDataMap;
    dataMap.Get("param")

      3. 在Job中获取参数还可以通过context.MergedJobDataMap来获取参数,但是如果同时通过JobDetail和trigger传递同一个参数,则只能获取到最后添加的那个参数,前面添加的参数会被丢弃,例如上面都添加了param参数,则下面代码获取到的值为value2

    JobDataMap dataMap = context.MergedJobDataMap;
    Console.WriteLine(dataMap.Get("param"));

      到这里,我们知道了通过Cron表达式的Trigger,虽然Cron可以很灵活的定制时间规则,但是Quartz.Net也提供了另一个常用的Trigger,那就是SimpleTrigger,它的使用方法如下,其表示每隔10秒钟执行一次,最多执行10次

    ITrigger trigger = TriggerBuilder.Create()
           .WithIdentity("trigger1", "group1")
           .StartNow()
           .WithSimpleSchedule(x => x
               .WithIntervalInSeconds(10)
               .WithRepeatCount(10)
               //.RepeatForever())
               .WithDescription("This is testjob's Trigger")
           .Build();

      我们知道,在Quartz.Net 3.0之前的版本有一个IStatefulJob接口,表示有状态的作业,意思是上一次的执行结果可以影响到下一次,而在3.0之后的版本,通过特性 PersistJobDataAfterExecution 来实现,下面代码中的DisallowConcurrentExecution 特性表示拒绝同一时间重复执行,即同一任务只能是串行的执行,避免的并发带来的问题。

    [PersistJobDataAfterExecution]//执行后保留数据,更新JobDataMap
    [DisallowConcurrentExecution]//拒绝同一时间重复执行,同一任务串行 public class TestStatefulJob : IJob { }

      Quartz.Net 提供了三个Listerner,即ISchedulerListener,IJobListener,ITriggerListener来分别各个环节的事件,我们可以分别来定义它们的三个实现类

        public class CustomSchedulerListener : ISchedulerListener
        {
            public async Task JobAdded(IJobDetail jobDetail, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(() =>
                {
                    Console.WriteLine($"This is {nameof(CustomSchedulerListener)} JobAdded {jobDetail.Description}");
                });
            }
    
            public Task JobDeleted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task JobInterrupted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task JobPaused(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task JobResumed(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task JobScheduled(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task JobsPaused(string jobGroup, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task JobsResumed(string jobGroup, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task JobUnscheduled(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task SchedulerError(string msg, SchedulerException cause, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task SchedulerInStandbyMode(CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task SchedulerShutdown(CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task SchedulerShuttingdown(CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public async Task SchedulerStarted(CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(() =>
                {
                    Console.WriteLine($"This is {nameof(CustomSchedulerListener)} SchedulerStarted ");
                });
            }
    
            public async Task SchedulerStarting(CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(() =>
                {
                    Console.WriteLine($"This is {nameof(CustomSchedulerListener)} SchedulerStarting ");
                });
            }
    
            public Task SchedulingDataCleared(CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task TriggerFinalized(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task TriggerPaused(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task TriggerResumed(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task TriggersPaused(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
    
            public Task TriggersResumed(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken))
            {
                throw new NotImplementedException();
            }
        }
        public class CustomJobListener : IJobListener
        {
            public string Name => "CustomJobListener";
    
            public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(()=> {
                    Console.WriteLine($"CustomJobListener JobExecutionVetoed {context.JobDetail.Description}");
                });
            }
    
            public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(() => {
                    Console.WriteLine($"CustomJobListener JobToBeExecuted {context.JobDetail.Description}");
                });
            }
    
            public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(() => {
                    Console.WriteLine($"CustomJobListener JobWasExecuted {context.JobDetail.Description}");
                });
            }
        }
        public class CustomTriggerListener : ITriggerListener
        {
            public string Name => "CustomTriggerListener";
    
            public async Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(() =>
                {
                    Console.WriteLine($"CustomTriggerListener TriggerComplete {trigger.Description}");
                });
            }
    
            public async Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(() =>
                {
                    Console.WriteLine($"CustomTriggerListener TriggerFired {trigger.Description}");
                });
            }
    
            public async Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(() =>
                {
                    Console.WriteLine($"CustomTriggerListener TriggerMisfired {trigger.Description}");
                });
            }
    
            /// <summary>
            /// 要不要放弃job
            /// </summary>
            /// <param name="trigger"></param>
            /// <param name="context"></param>
            /// <param name="cancellationToken"></param>
            /// <returns></returns>
            public async Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
            {
                await Task.Run(() =>
                {
                    Console.WriteLine($"CustomTriggerListener TriggerMisfired {trigger.Description}");
                });
                return false;//false才能继续执行
            }
        }

      之后我们需要在创建Scheduler的时候将其添加到scheduler中

    StdSchedulerFactory factory = new StdSchedulerFactory();
    IScheduler scheduler = await factory.GetScheduler();
    scheduler.ListenerManager.AddSchedulerListener(new CustomSchedulerListener());
    scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener());
    scheduler.ListenerManager.AddJobListener(new CustomJobListener());
    await scheduler.Start();

      至此,我们的demo中都是使用console来自定义的记录操作过程,但是Quartz.Net也提供了一个日志组件 ILogProvider,我们创建一个CustomConsoleLogProvider,其实现了ILogProvider

        public class CustomConsoleLogProvider : ILogProvider
        {
            public Logger GetLogger(string name)
            {
                return (level, func, exception, parameters) =>
                {
                    if (level >= LogLevel.Info && func != null)
                    {
                        Console.WriteLine($"[{ DateTime.Now.ToLongTimeString()}] [{ level}] { func()} {string.Join(";", parameters.Select(p => p == null ? " " : p.ToString()))}  自定义日志{name}");
                    }
                    return true;
                };
            }
            public IDisposable OpenNestedContext(string message)
            {
                throw new NotImplementedException();
            }
    
            public IDisposable OpenMappedContext(string key, string value)
            {
                throw new NotImplementedException();
            }
        }

      然后通过Quartz.Logging.LogProvider来添加自定义的LogProvider,其在运行中会通过GetLogger来记录日志。

    LogProvider.SetCurrentLogProvider(new CustomConsoleLogProvider());

       到这里关于Quartz.Net的基本使用就介绍到这里,其进阶待下一篇再继续。

  • 相关阅读:
    《20171122-构建之法:现代软件工程-阅读笔记》
    课后作业-阅读任务-阅读提问-5
    结队-贪吃蛇-项目进度
    《团队-爬取豆瓣Top250电影-团队-阶段互评》
    zip相关知识梳理(一)
    C++入职学习篇--代码规范(持续更新)
    C++入职学习篇--新员工入职(持续更新)
    Node.js学习之TCP/IP数据通讯
    Node.js之操作文件系统(二)
    Node.js之操作文件系统(一)
  • 原文地址:https://www.cnblogs.com/jesen1315/p/11645218.html
Copyright © 2020-2023  润新知