首页 文章

使用Quartz进行调度

提问于
浏览
2

我们有一个.Net项目,用于检查远程计算机上是否存在文件 . 我们需要每天在预定义的时间对部门内的多台远程计算机(数千台)执行此操作 . 执行时间在数据库中指定,该数据库经常变化并且每个远程计算机的执行时间将不同(或者它们中的一些可能是相同的) . 为实现这一目标,我们计划使用Quartz调度程序 . 由于我们是Quartz的新手,我们想知道如何实现这一目标 . 从高层次来说,我们需要这些 -

  • 调度程序应该在每天的特定时间开始 - Quartz会这样做吗?

  • 一旦启动,它应该从DB获取每个远程计算机的执行时间

  • 使用所有远程计算机的列表及其执行时间准备xml

  • 为每台远程计算机安排执行

  • 在每个远程计算机的预定时间执行.Net项目

实现上述目标需要哪些类型的项目/组件?我们已经有一个.net类lib项目来检查远程计算机 . 我们如何与Quartz集成?


更新

非常感谢格拉纳达科德

根据我的理解,有一个主要工作每天运行一次(基于quartz.config)并通过从DB获取详细信息来安排其他工作 . 在这种情况下,控制台应用程序需要一直运行...您对编写控制台应用程序并使用任务计划程序安排在每天早上12点运行时有何看法?

在控制台应用程序中,我们将准备一个包含作业列表的(自定义)xml(包括作业类所需的触发时间和数据等详细信息)并将其传递给调度程序模块(类lib项目),它将启动调度程序并将xml中的所有作业排队 .

在安排所有这些作业之后,我们将等待(在调度程序模块内部)从所有作业中获取作业完成通知,然后关闭调度程序并退出控制台应用程序 . 这可能需要很长时间,具体取决于上一个作业的触发时间 .

让我知道你对这种方法的看法 .

另外,我们有多个部门(总共4个),所以我正在考虑编写4个控制台应用程序 - 每个部门一个 . 并使用任务调度程序安排所有这些(同时不同的时间可能没有帮助,因为每个部门可能有触发时间跨越整天的工作) .

或者,我也想知道是否可以在quartz.config文件中指定具有相同触发时间的4个作业? (不确定这将如何工作,它是否会创建4个dept特定的调度程序实例,我们可以将部门明智的作业排队到每个调度程序实例?)

1 回答

  • 2
    • 编写.xml中定义的作业,该作业是执行作业清理和(重新)调度的作业 . (下面代码中的“ScheduleOtherJobsJob”)

    • ScheduleOtherJobsJob将清除所有旧条目 . 它读取了一些数据存储区并找出了它必须执行的新作业列表 . 它会将这些作业添加到调度程序中 .

    我写了一个基本的例子 . 我没有花哨的基于运行的精确日期逻辑....我只是推进未来的WithIntervalInSeconds .

    在重新添加之前,我正在使用GROUP_NAME来确定要从调度程序中删除哪些作业 .

    我为ScheduleOtherJobsJob显示.xml . 您可以将其添加到AdoStore或其他任何地方 . .xml只是举例来说更简单 .

    请记住,您需要一个让Scheduler“活着”的进程 . Aka,您无法向IScheduler添加新作业,然后终止托管过程 . 在Console.App中,您将编写“Console.Readline()”...因此程序不会停止运行 .

    关键点是......安排其他工作的一项工作 . 根据某些过滤器(此处为GROUP_NAME)清除旧作业 . 根据某些数据存储重新添加作业......并确保主机进程保持运行,以便运行所有新计划的作业 .

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    using Quartz;
    using Quartz.Impl.Matchers;
    
    namespace MyNamespace
    {
    
    
        public class ScheduleOtherJobsJob : IJob
        {
    
            private const string GROUP_NAME = "MySpecialGroupName";
    
            /// <summary> 
            /// Called by the <see cref="IScheduler" /> when a
            /// <see cref="ITrigger" /> fires that is associated with
            /// the <see cref="IJob" />.
            /// </summary>
            public virtual void Execute(IJobExecutionContext context)
            {
    
                JobKey key = context.JobDetail.Key;
    
                JobDataMap jbDataMap = context.JobDetail.JobDataMap;
    
        //            string jobSays = jbDataMap.GetString("KeyOne");
    
                JobDataMap trgDataMap = context.Trigger.JobDataMap;
    
                string triggerParameter001 = trgDataMap.GetString("TriggerFileName");
    
                JobDataMap mergedMap = context.MergedJobDataMap;
    
                string whoWins = mergedMap.GetString("DefinedInJobDetailAndTriggerKey");
    
                string msg = string.Format("HasParametersJob : JobKey='{0}', jobSays='{1}', jobDetailParameter001='{2}', triggerParameter001='{3}', triggerParameter002='{4}' , whoWins='{5}' at '{6}'", key, jobSays, jobDetailParameter001, triggerParameter001, triggerParameter002, whoWins, DateTime.Now.ToLongTimeString());
                Console.WriteLine(msg);
    
                /* */
                context.Scheduler.UnscheduleJobs(GetAllJobTriggerKeys(context.Scheduler));
    
                /* Schedule Your Jobs  */
                List<OtherJobInfo> infos = new OtherJobInfoData().GetOtherJobs();
                foreach (OtherJobInfo info in infos)
                {
                    ScheduleAHasParametersJob(context.Scheduler, info);
                }
    
            }
    
    
            private IList<TriggerKey> GetAllJobTriggerKeys(IScheduler scheduler)
            {
                /* Find all current jobs.....filter here if need be */
    
                IList<TriggerKey> returnItems = new List<TriggerKey>();
    
                IList<string> jobGroups = scheduler.GetJobGroupNames();
                //IList<string> triggerGroups = scheduler.GetTriggerGroupNames();
    
                IList<string> filteredJobGroups = jobGroups.Where(g => g.Equals(GROUP_NAME)).ToList();
    
                foreach (string group in filteredJobGroups)
                {
                    var groupMatcher = GroupMatcher<JobKey>.GroupContains(group);
                    var jobKeys = scheduler.GetJobKeys(groupMatcher);
                    foreach (var jobKey in jobKeys)
                    {
                        var detail = scheduler.GetJobDetail(jobKey);
    
    
                        var triggers = scheduler.GetTriggersOfJob(jobKey);
                        foreach (ITrigger trigger in triggers)
                        {
                            returnItems.Add(trigger.Key);
    
                            Console.WriteLine(group);
                            Console.WriteLine(jobKey.Name);
                            Console.WriteLine(detail.Description);
                            Console.WriteLine(trigger.Key.Name);
                            Console.WriteLine(trigger.Key.Group);
                            Console.WriteLine(trigger.GetType().Name);
                            Console.WriteLine(scheduler.GetTriggerState(trigger.Key));
                            DateTimeOffset? nextFireTime = trigger.GetNextFireTimeUtc();
                            if (nextFireTime.HasValue)
                            {
                                Console.WriteLine(nextFireTime.Value.LocalDateTime.ToString());
                            }
    
                            DateTimeOffset? previousFireTime = trigger.GetPreviousFireTimeUtc();
                            if (previousFireTime.HasValue)
                            {
                                Console.WriteLine(previousFireTime.Value.LocalDateTime.ToString());
                            }
                        }
                    }
                }
    
                return returnItems;
            }
    
    
    
            private static void ScheduleAHasParametersJob(IScheduler sched, OtherJobInfo info)
            {
    
                IJobDetail hasParametersJobDetail = JobBuilder.Create<FileNameDoSomethingJob>()
                    .WithIdentity(info.UniqueIdentifier + "IJobDetailWithIdentity", GROUP_NAME)
                    //.UsingJobData("JobDetailFileName", info.FileName)
                    .Build();
    
    
                ITrigger hasParametersJobTrigger001 = TriggerBuilder.Create()
                  .WithIdentity(info.UniqueIdentifier + "ITriggerWithIdentity", GROUP_NAME)
                    .UsingJobData("TriggerFileName", info.FileName)
                  .StartNow()
                  .WithSimpleSchedule(x => x
                      .WithIntervalInSeconds(info.WithIntervalInSeconds) /* You'll have to do something fancier here with scheduling if you want an exact time */
                      .WithRepeatCount(0))
                  .Build();
    
    
    
                sched.ScheduleJob(hasParametersJobDetail, hasParametersJobTrigger001);
            }
    
        }
    
    
    
    
        public class FileNameDoSomethingJob : IJob
        {
            public virtual void Execute(IJobExecutionContext context)
            {
                JobKey key = context.JobDetail.Key;
    
                JobDataMap jbDataMap = context.JobDetail.JobDataMap;
    
                JobDataMap trgDataMap = context.Trigger.JobDataMap;
    
                string triggerFileNameParameter = trgDataMap.GetString("TriggerFileName");
    
                JobDataMap mergedMap = context.MergedJobDataMap;
    
                string msg = string.Format("HasParametersJob : JobKey='{0}', triggerFileNameParameter='{1}' at '{2}'", key, triggerFileNameParameter, DateTime.Now.ToLongTimeString());
                Console.WriteLine(msg);
            }
        }
    
    
    
    
        public class OtherJobInfoData
        {
    
            public List<OtherJobInfo> GetOtherJobs()
            {
                List<OtherJobInfo> returnItems = new List<OtherJobInfo>();
    
                OtherJobInfo oji1 = new OtherJobInfo() { UniqueIdentifier = "ABC123", WithIntervalInSeconds = 5, FileName = @"C:\file1.xml"};
                OtherJobInfo oji2 = new OtherJobInfo() { UniqueIdentifier = "DEF234", WithIntervalInSeconds = 5, FileName = @"C:\file2.xml" };
                OtherJobInfo oji3 = new OtherJobInfo() { UniqueIdentifier = "GHI345", WithIntervalInSeconds = 5, FileName = @"C:\file3.xml" };
    
                returnItems.Add(oji1);
                returnItems.Add(oji2);
                returnItems.Add(oji3);
    
                return returnItems;
            }
        }
    
        public class OtherJobInfo
        {
            public string UniqueIdentifier { get; set; }
            public int WithIntervalInSeconds { get; set; }
            public string FileName { get; set; }
        }
    
    }
    

    还有一些xml来运行ScheduleOtherJobsJob

    <?xml version="1.0" encoding="UTF-8"?>
    
    <job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
    
    
        <!-- This value wipes out existing jobs...be very careful with it being "true"  -->
        <processing-directives>
            <overwrite-existing-data>true</overwrite-existing-data>
        </processing-directives>
    
        <schedule>
    
    
    
            <job>
                <name>ScheduleOtherJobsJobName</name>
                <group>ScheduleOtherJobsJobGroupName</group>
                <description>My Description</description>
                <job-type>MyNamespace.ScheduleOtherJobsJob, MyAssembly</job-type>
                <durable>true</durable>
                <recover>false</recover>
                <job-data-map>
                </job-data-map>
            </job>
            <trigger>
    
                <simple>
                    <name>ScheduleOtherJobsJobTriggerName</name>
                    <group>ScheduleOtherJobsJobTriggerGroup</group>
                    <description>My ScheduleOtherJobsJobTriggerName Description</description>
                    <job-name>ScheduleOtherJobsJobName</job-name>
                    <job-group>ScheduleOtherJobsJobGroupName</job-group>
    
    
                    <job-data-map>
                    </job-data-map>
    
                    <!--<start-time>1982-06-28T18:15:00.0Z</start-time>-->
                    <!--<end-time>2020-05-04T18:13:51.0Z</end-time>-->
                    <misfire-instruction>SmartPolicy</misfire-instruction>
                    <!-- repeat indefinitely every 5 seconds -->
                    <repeat-count>-1</repeat-count>
                    <repeat-interval>5000</repeat-interval>
    
    
    
                </simple>
    
            </trigger>
    
        </schedule>
    
    
    
    </job-scheduling-data>
    

    编辑:APPEND

    确保您的app.config(或web.config)指向quartz-configuration文件 . 这是我的顶级......注意,它应该作为指导.....

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
    
    
        <configSections>
            <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    
    
        </configSections>
    
    
    
    
        <quartz configSource="MyQuartzConfiguration.config" />
    

    然后MyQuartzConfiguration.config文件需要一些东西,最重要的是“Quartz_Jobs_001.xml”(名称并不重要,但此文件包含作业和触发器的信息 .

    <add key="quartz.plugin.jobInitializer.type" value="Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin" />
    <add key="quartz.scheduler.instanceName" value="DefaultQuartzScheduler" />
    <add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz" />
    <add key="quartz.threadPool.threadCount" value="10" />
    <add key="quartz.threadPool.threadPriority" value="2" />
    <add key="quartz.jobStore.misfireThreshold" value="60000" />
    <add key="quartz.jobStore.type" value="Quartz.Simpl.RAMJobStore, Quartz" />
    <add key="quartz.plugin.jobInitializer.fileNames" value="Quartz_Jobs_001.xml" />
    <add key="quartz.plugin.jobInitializer.failOnFileNotFound" value="true" />
    <add key="quartz.plugin.jobInitializer.scanInterval" value="120" />
    

相关问题