技术 思绪 摘录 旅行
一般工作中,经常会遇到用Quartz写一些小工具,来实时处理程序不能及时处理的问题,比如扫描哪些订单支付了,但是接受通知失败,状态没改为已支付的,还有定时通知任务、定时统计任务,这些场景都很常见。我这篇博文讲述的不是这些一成不变的场景,而是后台可控,动态的场景,举个例子:项目一期要求我们的定时统计任务,要在晚上10点钟开始,项目二期,需求变了,要求在9点钟开始,项目三期,需求又变了,要求在8点开始,也就是任务调度有些东西变成了动态的。这时候我们需要怎么处理呢?

举例场景:有一个喝水提醒的任务,每15秒钟提醒一次,后台在任务运行中改成了18秒提醒一次,我们需要更新这个喝水提醒的任务。

解决方案:

    除了创建一个喝水提醒的任务之外,再加一个任务管理任务,这个调度任务负责来监听配置,如果检测到配置发生了变化,则更新喝水提醒任务。


1、创建控制台程序。

2、引入Quartz的组件。

3、编写Program.cs,让任务跑起来。

using System;
using System.Threading;
using Quartz;
using Quartz.Impl;

namespace QuartzManager
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread taskThread = new Thread(StartTask);
            taskThread.Start();

            Console.Out.WriteLine("");
            Console.Out.WriteLine("");
            Console.Out.WriteLine("        ***************************************");
            Console.Out.WriteLine("        **                                   **");
            Console.Out.WriteLine("        **             任务启动成功           **");
            Console.Out.WriteLine("        **                                   **");
            Console.Out.WriteLine("        ***************************************");
            Console.Out.WriteLine("");
            Console.Out.WriteLine("");
            Console.WriteLine("         启动时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            Console.Out.WriteLine("");
            Console.WriteLine("         若需退出请输入 exit 按回车退出...\r\n");
            string userCommand = string.Empty;
            while (userCommand != "exit")
            {
                if (string.IsNullOrEmpty(userCommand) == false)
                {
                    Console.WriteLine("                非退出指令,自动忽略...");
                }
                userCommand = Console.ReadLine();
            }
        }

        /// <summary>
        /// Starts the task.
        /// </summary>
        private static async void StartTask()
        {
            ISchedulerFactory sf = new StdSchedulerFactory();
            if (SchedulerUtil._scheduler == null)
            {
                SchedulerUtil._scheduler = await sf.GetScheduler();
            }
            await SchedulerUtil._scheduler.Start();

            #region 调度任务
            //创建任务对象
            IJobDetail job1 = JobBuilder.Create<TaskManagerJob>().WithIdentity("job1", "group1").Build();
            //创建触发器
            ITrigger trigger1 = TriggerBuilder.Create().WithIdentity("trigger1", "group1").StartNow()
                .WithCronSchedule("0/10 * * * * ?").Build();
            #endregion

            #region 任务 喝水提醒
            //创建任务对象
            IJobDetail job2 = JobBuilder.Create<TaskDrinkTipsJob>().WithIdentity("job2", "group2").Build();
            //创建触发器
            ITrigger trigger2 = TriggerBuilder.Create().WithIdentity("trigger2", "group2").StartNow()
                .WithCronSchedule("0/15 * * * * ?").Build();
            #endregion

            await SchedulerUtil._scheduler.ScheduleJob(job1, trigger1);
            await SchedulerUtil._scheduler.ScheduleJob(job2, trigger2);
        }
    }
}

这里我设置了喝水提醒任务15秒钟(0/15 * * * * ?)提醒一次,命名为job2、group2、trigger2,组、任务、触发器都命名为2,调度任务都命名为1。

还有个辅助类:SchedulerUtil.cs,来获取Job的corn时间表达式,以及用静态变量存储了IScheduler对象,方便公共读取。

using System.Threading.Tasks;
using Quartz;
using Quartz.Impl.Triggers;

namespace QuartzManager
{
    public class SchedulerUtil
    {
        public static IScheduler _scheduler = null;

        /// <summary>
        ///获取CronStr
        /// </summary>
        /// <param name="triggername">The triggername.</param>
        /// <param name="groupname">The groupname.</param>
        /// <returns></returns>
        public static async Task<string> GetCornString(string triggername, string groupname)
        {
            ITrigger jobKeySet = await _scheduler.GetTrigger(new TriggerKey(triggername, groupname));
            CronTriggerImpl cti = (jobKeySet as CronTriggerImpl);
            return cti?.CronExpressionString ?? "";
        }
    }
}

4、编写喝水任务类

using System;
using System.Threading.Tasks;
using Quartz;

namespace QuartzManager
{
    /// <summary>
    /// 喝水提醒任务
    /// </summary>
    /// <seealso cref="IJob" />
    public class TaskDrinkTipsJob : IJob
    {
        public virtual Task Execute(IJobExecutionContext context)
        {
            return Task.Factory.StartNew(EveryTask);
        }

        /// <summary>
        /// 任务实现
        /// </summary>
        public static void EveryTask()
        {
            Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss}-该喝水了哦");
        }
    }
}

每次都打印出喝水提醒的时间,方便我们看目前用的哪个时间。

5、编写调度任务的代码

using System;
using System.Threading.Tasks;
using Quartz;

namespace QuartzManager
{
    /// <summary>
    /// 调度任务
    /// </summary>
    /// <seealso cref="IJob" />
    public class TaskManagerJob : IJob
    {
        public virtual Task Execute(IJobExecutionContext context)
        {
            return Task.Factory.StartNew(EveryTask);
        }

        private static int actioncount = 0;
        /// <summary>
        /// 任务实现
        /// </summary>
        public static async Task EveryTask()
        {
            actioncount += 1;
            Console.WriteLine($"调度任务第{actioncount}次执行");
            //任务管理代码
            if (actioncount>=5) //模拟服务器在运行中改了配置  比如 从每分钟15秒执行 改成了 每分钟18秒执行
            {
                //读取配置
                string servertime = "0/18 * * * * ?";//模拟服务器改成了18秒提醒一次

                //首先获取喝水任务的时间设置
                string crontime =await SchedulerUtil.GetCornString("trigger2", "group2");
                if (crontime!= servertime)
                {
                    await SchedulerUtil._scheduler.DeleteJob(new JobKey("job2", "group2"));

                    //创建任务对象
                    IJobDetail job2 = JobBuilder.Create<TaskDrinkTipsJob>().WithIdentity("job2", "group2").Build();
                    //创建触发器
                    ITrigger trigger2 = TriggerBuilder.Create().WithIdentity("trigger2", "group2").StartNow()
                        .WithCronSchedule(servertime).Build();
                    await SchedulerUtil._scheduler.ScheduleJob(job2, trigger2);
                    Console.WriteLine($"调度任务更新了喝水任务");
                }
            }

        }
    }
}

看结果

image.png

第五次调度任务提示了更新,再看喝水提醒的时间,已经是18秒一次进行执行了。

这里面核心思想就是,如果任务发生了变化,那就删除原来的任务,创建新的任务,而不是在原来任务进行编辑。


CarsonIT 微信扫码关注公众号 策略、创意、技术

留下您的脚步

 

最近评论

查看更多>>

精选推荐

阅读排行

友情打赏

请打开您的微信,扫一扫