我们有一个.Net项目,用于检查远程计算机上是否存在文件。 我们需要每天在预定义的时间对部门内的多台远程计算机(数千台)执行此操作。执行时间在数据库中指定,该数据库经常变化并且每个远程计算机的执行时间将不同(或者它们中的一些可能是相同的)。 为实现这一目标,我们计划使用Quartz调度程序。由于我们是Quartz的新手,我们想知道如何实现这一目标。在高层次上,我们需要这些 -
实现上述目标需要哪些类型的项目/组件?我们已经有一个.net类lib项目来检查远程计算机。我们如何与Quartz集成?
非常感谢格拉纳达·科德尔
根据我的理解,有一个主要工作每天运行一次(基于quartz.config)并通过从DB获取详细信息来安排其他工作。在这种情况下,控制台应用程序必须始终运行... 您对编写控制台应用程序并使用任务计划程序安排在每天上午12点运行时有何看法?
在控制台应用程序中,我们将准备一个包含作业列表的(自定义)xml(包含作业类所需的触发时间和数据等详细信息)并将其传递给调度程序模块(类lib项目) 'll start scheduler&排队xml中的所有作业。
在安排所有这些作业之后,我们将等待(在调度程序模块内部)从所有作业中获取作业完成通知,然后关闭调度程序并退出控制台应用程序。这可能需要很长时间,具体取决于上一个作业的触发时间。
让我知道您对此方法的看法。
此外,我们有多个部门(总共4个),所以我正在考虑编写4个控制台应用程序 - 每个部门一个。并使用任务调度程序安排所有这些计划程序(同时,不同的计时可能没有帮助,因为每个部门可能有触发时间跨越整天的工作)。
或者,我也想知道是否可以在quartz.config文件中指定4个具有相同触发时间的作业? (不确定这将如何工作,它是否会创建4个dept特定的调度程序实例,我们可以将部门明智的作业排队到每个调度程序实例?)
答案 0 :(得分: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; }
}
}
运行ScheduleOtherJobsJob的一些xml
<?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)指向石英配置文件。这是我的顶部......注意,它应该作为指导........
<?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" />