DotNetCore跨平台~Quartz定时单次任务

之前写过一篇文件《DotNetCore跨平台~Quartz热部署的福音~监控文件夹的变化》,今天主要把框架优化了一下,支持外部触发,并支持外部将参数以JobDataMap形式进行输入,然后在咱们的Job里进行使用它,故称参数化任务。

Quartz使用场景:

  1. 定时单次任务:在未来某个时间去执行一次
  2. 定点任务  :在某个时间去执行,可以是轮询的
  3. 周期任务  :按某个时间间隔去轮询执行

今天说的外部触发的任务是指第一种,即在未来某个时间点去执行,并且只执行一次。说一下思路,这种任务某个JobBase的子类,它需要重写属性IsSingle,将值设为1表示单次任务,然后在Quartz启动后,它会被立即执行,执行完成后,销毁!

用例:你可以在quartz调度中心里对外公开一些方法,让你的Job依赖于某个时间点和参数去执行,执行一次就停止,这样我们的调度就更加灵活了。

为单次任务添加了IsSingle属性

    [DisallowConcurrentExecution()]
    public abstract class JobBase : ISchedulingJob
    {
        #region Properties

        /// <summary>
        /// 取消资源
        /// </summary>
        public CancellationTokenSource CancellationSource => new CancellationTokenSource();

        /// <summary>
        /// 执行计划,除了立即执行的JOB之后,其它JOB需要实现它
        /// </summary>
        public virtual string Cron => "* * * * * ?";

        /// <summary>
        /// 是否为单次任务,黑为false
        /// </summary>
        public virtual bool IsSingle => false;

        /// <summary>
        /// Job的名称,默认为当前类名
        /// </summary>
        public virtual string JobName => GetType().Name;

        /// <summary>
        /// Job执行的超时时间(毫秒),默认5分钟
        /// </summary>
        public virtual int JobTimeout => 5 * 60 * 1000;

        #endregion Properties

        #region Methods

        /// <summary>
        /// Job具体类去实现自己的逻辑
        /// </summary>
        protected abstract void ExcuteJob(IJobExecutionContext context, CancellationTokenSource cancellationSource);

        /// <summary>
        /// 当某个job超时时,它将被触发,可以发一些通知邮件等
        /// </summary>
        /// <param name="arg"></param>
        private void CancelOperation(object arg)
        {
            CancellationSource.Cancel();
            StdSchedulerFactory.GetDefaultScheduler().Result.Interrupt(new JobKey(JobName));
            Console.WriteLine(JobName + "Job执行超时,已经取消,等待下次调度...");
        }

        #endregion Methods

        #region IJob 成员

        public Task Execute(IJobExecutionContext context)
        {
            Timer timer = null;
            try
            {
                timer = new Timer(CancelOperation, null, JobTimeout, Timeout.Infinite);
                Console.WriteLine(DateTime.Now.ToString() + "{0}这个Job开始执行", context.JobDetail.Key.Name);
                if (context.JobDetail.JobDataMap != null)
                {
                    foreach (var pa in context.JobDetail.JobDataMap)
                        Console.WriteLine($"JobDataMap,key:{pa.Key},value:{pa.Value}");
                }
                ExcuteJob(context, CancellationSource);
            }
            catch (Exception ex)
            {
                Console.WriteLine(this.GetType().Name + "error:" + ex.Message);
            }
            finally
            {
                if (timer != null) timer.Dispose();
            }
            return Task.CompletedTask;
        }

        #endregion
    }

统一的加入Job队列的方法

在我们之前的QuartzManager管理者中,我们需要添加对单次任务的支持,这点我们将任务加入到quartz的代码进行了重构,提取到了方法里。

        /// <summary>
        /// 将类型添加到Job队列
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="dt">时间点</param>
        /// <param name="param">参数</param>
        private static void JoinToQuartz(Type type, DateTimeOffset dt, Dictionary<string, object> param = null)
        {
            var obj = Activator.CreateInstance(type);
            if (obj is ISchedulingJob)
            {
                var tmp = obj as ISchedulingJob;
                string cron = tmp.Cron;
                string name = tmp.JobName;
                var cancel = tmp.CancellationSource;

                var jobDetail = JobBuilder.Create(type)
                                          .WithIdentity(name)
                                          .Build();
                if (param != null)
                    foreach (var dic in param)
                        jobDetail.JobDataMap.Add(dic.Key, dic.Value);

                ITrigger jobTrigger;
                if (tmp.IsSingle)
                {
                    jobTrigger = TriggerBuilder.Create()
                                               .WithIdentity(name + "Trigger")
                                               .StartAt(dt)
                                               .Build();
                }
                else
                {
                    jobTrigger = TriggerBuilder.Create()
                                                .WithIdentity(name + "Trigger")
                                                .StartNow()
                                                .WithCronSchedule(cron)
                                                .Build();
                }
                StdSchedulerFactory.GetDefaultScheduler().Result.ScheduleJob(jobDetail, jobTrigger, cancel.Token);
                LoggerInfo($"->任务模块{name}被装载...", ConsoleColor.Yellow);
            }
        }

对外公开的参数化接口

而对于外界如果希望再次触发这个单次任务,我们可以在QuartzManager里公开一个方法,用来向当前SchedulerFactory里添加新的Job就可以了,这个方法很简单,可以提供一个默认的时间策略,如默认为1分钟后执行,也可以自己控制时间。

      /// <summary>
        /// 任务在1分钟之后被执行1次
        /// </summary>
        /// <param name="type"></param>
        /// <param name="job"></param>
        /// <param name="param"></param>
        public static void SignalJob(Type type, Dictionary<string, object> param)
        {
            SignalJob(type, DateTimeOffset.Now.AddSeconds(10), param);
        }

        /// <summary>
        /// 任务在某个时间之后被执行1次
        /// </summary>
        /// <param name="type"></param>
        /// <param name="job"></param>
        /// <param name="offset"></param>
        /// <param name="param"></param>
        public static void SignalJob(Type type, DateTimeOffset offset, Dictionary<string, object> param)
        {
            JoinToQuartz(type, offset);
        }

那么,现在某个任务调度中心就更加完善了,开发人员在使用时也很简单,只要继承JobBase,或者去实现ISchedulingJob接口就可以了,非常灵活!

感谢各位的阅读!

quartz,dotnet core我们还在继续研究的路上!

本文转自博客园张占岭(仓储大叔)的博客,原文链接:DotNetCore跨平台~Quartz定时单次任务,如需转载请自行联系原博主。

时间: 2024-09-21 16:07:43

DotNetCore跨平台~Quartz定时单次任务的相关文章

DotNetCore跨平台~Quartz热部署的福音~监控文件夹的变化

在DotNetCore出来之后,同时也使用了quartz进行调度中心的设计,将它做到docker里方便部署,在之前的quartz版本里支持配置文件的方式,而现在不支持了,我们应该去想一下,为什么不去支持配置文件?当然大叔也为配置文件设计了支持的方式,但我们还是应该想想作者为什么不去支持配置? 热插拔,服务发现? 和上面两个概念可能有点关系,热插拔很容易理解,就是把dll模块放到正在运行的项目时,它可以直接启动,这个功能对调度中心来说,很是必要,因为你可能需要按着不同的功能设计一些服务job,而这

DotNetCore跨平台~文章索引~永久更新

本索引目录主要包括仓储大叔对dotnet core架构的研究与知识积累,从2016年开始进行撰写,到今天已经有一年多了,其中有一些小知识,小技巧,小应用,希望给大家在开发时一些启发,也希望dotnet core越来越好,希望2.0正式版快点出来! 我们的框架应该是基于组件化的! 我们的系统应该是基于微服务化的! 我们的部署,应该是基于自动化的! DotNetCore跨平台目录 DotNetCore跨平台~Startup类的介绍(2016-05-31 16:25) Linux~centos上安装.

DotNetCore跨平台~组件化时代来了

进行dotnetcore之后,各种对象都是基于DI进行生产的,这就有了对象的生命周期一说,早在autofac里也有相关知识点,这与Microsoft.Extensions.DependencyInjection是完全温和的,方便大家理解,在讲今天的组件化之前,先对DI的三种生命周期进行理解一下: AddSingleton:单例,进程内它是唯一的 AddTransient:瞬息,在对象在当前环境内,作用域内是唯一的 AddScoped:请求,对象在一个HTTP请求内是唯一的 下面来看今天的组件化的

DotNetCore跨平台~在appsettings.json里自定义配置项

DotNetCore里一切都是依赖注入的,对于appsettings这个可扩展的配置对象也不例外,它位于项目根目录,一般在startup里去注册它,在类中通过构造方法注入来获取当前的对象,以便去使用它,当然我们也可以自己去构建和使用它,下面我就来总结一下. 传统方法,startup注入,构造方法使用 1 注意配置类 public class RedisConfiguration { #region 属性成员 /// <summary> /// 文件上传路径 /// </summary&g

Quartz定时被二次触发的问题

其中<Host/>告诉tomcat,在启动的时候加载webapps下的所有项目工程文件,<Context/>又让tomcat再加载了一遍(一般情况下配置<Context/>,主要是由于想域名访问时将工程名去掉的原因配置),这种情况下会导致工程中的quartz定时被两次触发,执行两次.   <Host/>里面的改成 autoDeploy="false" deployOnStartup="false" 这样就可以避免tom

配置-SpringBatch 和 quartz定时,quartz调用多个springbatchjob异常,

问题描述 SpringBatch 和 quartz定时,quartz调用多个springbatchjob异常, 异常如下: 2015-05-12 11:49:30.046 [startQuertz_Worker-5] ERROR org.quartz.core.JobRunShell - Job DEFAULT.busBatchJobDetail threw an unhandled Exception: org.springframework.dao.CannotSerializeTransa

DotNetCore跨平台~EFCore连接Mysql的方式

在.net frameworks的ef里连接mysql我们已经测试通过了,而在dotnet core里的efCore上去连接mysql我们需要测试一下,并且在测试过程中出现了一些问题,当然最后也是解决了,下面总结一下,分享给大家! mysql项目的依赖包 数据上下文和连接串 数据仓储 添加模块扩展 业务层注入 业务实现 mysql项目的依赖包 Microsoft.EntityFrameworkCore MySql.Data.EntityFrameworkCore 数据上下文和连接串 对于mysq

DotNetCore跨平台~服务总线_事件总线的重新设计

理论闲话 之前在.netFramework平台用的好好的,可升级到.net core平台之后,由于不再需要二进制序列化,导致咱们的事件机制遇到了问题,之前大叔的事件一直是将处理程序序列化后进行存储的,处理存储的参数为事件源,一个事件源可以由多个处理程序订阅,当事件源被发布时,这些被序列化的代码段会被回调执行,这是大叔之前的思路,在RedisBus和MemoryBus里已经得到了实现,读过大叔源代码的同学应该有所了解了. 事件源和处理程序 /// <summary> /// 事件源 /// &l

DotNetCore跨平台~问题~NETCoreAPP, Version=v1.0&#039; compatible with one of the target runtimes: &#039;win10-x64

新建console项目之后,编译程序出现以下错误: Can not find runtime target for framework '.NETCoreAPP, Version=v1.0' compatible with one of the target runtimes: 'win10-x64, win81-x64, win8-x64, win7-x64'. Possible causes:        The project has not been restored or resto