在所有 Quartz .NET IInterruptableJob 上触发中断
Trigging Interrupt on all Quartz .NET IInterruptableJob
我正在使用 Quartz 调度程序,并试图在关闭应用程序时关闭所有作业。我有一项专业工作是做 'Hold' 或 'Busy-wait',基本上直到它得到一个条件,它才会耐心地坐在那里等待。
由于新的集成点,这项工作是新的。该应用程序 运行 作为使用 Topshelf 的服务,每当我们尝试关闭该服务以对其进行升级时,既然这项工作是 运行ning,我们最终必须重新启动服务器才能使其正常运行关机。
无论如何,这里变得很奇怪,我只有一个作业类型,当我尝试使用作业 FireInstanceId
或 JobKey
:[=16 在以下代码部分中触发中断时=]
_logger.InfoFormat("{0} scheduler interrupting listener", scheduler.SchedulerName);
scheduler.Interrupt(ListenerKeys.Realtime);
_logger.InfoFormat("{0} scheduler shutting down", scheduler.SchedulerName);
scheduler.Shutdown(true);
_logger.InfoFormat("{0} scheduler shut down", scheduler.SchedulerName);
我遇到异常:
Job 'Listeners.Realtime' can not be interrupted, since it does not implement Quartz.IInterruptableJob
人们会认为这是直截了当的。但是,这是唯一使用此作业密钥的作业:
ListenerJob : BaseJob, IInterruptableJob
{
// some other code referenced in ExecuteJob
public void Interrupt()
{
_dequeuer.StopDequeing();
}
}
我会冒昧地说这就是你实现它的方式,所以我的问题变成了:Quartz 中是否存在已知错误?组密钥和中断是否存在问题?有没有办法告诉调度程序中断所有可中断的作业?有其他选择吗?
更新
我决定 运行 下面的代码,以便从下面的答案中进行更多诊断。 var interfaces 实际上包括 IInterruptableJob
var jobs = scheduler.GetCurrentlyExecutingJobs().Where(x => Equals(x.JobDetail.Key, ListenerKeys.Realtime));
var job1 = jobs.First();
var interfaces = job1.JobDetail.JobType.GetInterfaces();
此外,我 运行 ReportInterruptableJob 如下所示,它检查了程序集并确认 ListenerJob 实现了接口。
更新 2:
好的,我去了 git 中心,运行 确切的 meshos。 Job.JobInstance as IInterruptableInterface returns null,这就是我收到错误的原因。我想我不明白的是,我是如何围绕 IJo 形成 JobInstance 的,它确实实现了 IInterruptableJob
UPDATE3:好的....所以我在 bootstrap 中发现了一些正在使用 JobWrapper<> 的东西。我对此一无所知,但我确定这是其中的一部分。
所以我同意另一个答案(C骑士)的说法,JobKey可能已经关闭了。
如果您已经实现了接口...并且您拥有正确的 JobKey..那么您应该不会得到该异常。
下面是我用于中断工作的代码。我也在尝试找到 "findthekey" 逻辑。
private static void InterruptAJob(JobKey foundJobKey, IScheduler sched)
{
if (null != foundJobKey)
{
sched.Interrupt(foundJobKey);
}
}
追加
这是我用于查找工作密钥的代码。
我将从它开始............放置一些断点......并查看您的 JobKey 是否在集合中。也许修改例程(在你找出神奇的地方之后)以根据特定标准找到工作关键......如果你没有找到你期望找到的东西则抛出异常。 Aka,不要 "assume" JobKey 存在......但是查询它并抛出适当的异常(或者如果没有找到匹配则编写适当的 == null 逻辑)......vs "assume it has to be there" 方法。
同样,以下是 "starter" 代码......我的概念验证只有一项工作,所以我不必具体说明。
private static JobKey FindaJobKey(IScheduler sched, ILogger logger)
{
JobKey returnJobKey = null;
IList<string> jobGroupNames = sched.GetJobGroupNames();
if (null != jobGroupNames)
{
if (jobGroupNames.Count > 0)
{
GroupMatcher<JobKey> groupMatcher = GroupMatcher<JobKey>.GroupEquals(jobGroupNames.FirstOrDefault());
Quartz.Collection.ISet<JobKey> keys = sched.GetJobKeys(groupMatcher);
returnJobKey = keys.FirstOrDefault();
if (null == returnJobKey)
{
throw new ArgumentOutOfRangeException("No JobKey Found");
}
}
}
Thread.Sleep(TimeSpan.FromSeconds(1));
return returnJobKey;
}
追加:
也许是这样的:
private static JobKey FindJobKey(IScheduler sched, ILogger logger, string jobGroupName)
{
JobKey returnJobKey = null;
IList<string> jobGroupNames = sched.GetJobGroupNames();
if (null != jobGroupNames)
{
string matchingJobGroupName = jobGroupNames.Where(s => s.Equals(jobGroupName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (null != matchingJobGroupName)
{
GroupMatcher<JobKey> groupMatcher = GroupMatcher<JobKey>.GroupEquals(matchingJobGroupName);
Quartz.Collection.ISet<JobKey> keys = sched.GetJobKeys(groupMatcher);
if (null != keys)
{
if (keys.Count > 0)
{
throw new ArgumentOutOfRangeException(string.Format("More than one JobKey Found. (JobGroupName='{0}')", jobGroupName));
}
returnJobKey = keys.FirstOrDefault();
if (null != returnJobKey)
{
throw new ArgumentOutOfRangeException(string.Format("No JobKey Found. (JobGroupName='{0}')", jobGroupName));
}
}
}
}
Thread.Sleep(TimeSpan.FromSeconds(1));
return returnJobKey;
}
另一种快速而肮脏的 "look at what you got going on" 方法。
private static void ShowJobs(IScheduler sched)
{
Console.WriteLine("");
Console.WriteLine("ShowJobs : Start");
GroupMatcher<JobKey> matcherAll = GroupMatcher<JobKey>.AnyGroup();
Quartz.Collection.ISet<JobKey> jobKeys = sched.GetJobKeys(matcherAll);
foreach (JobKey jk in jobKeys)
{
Console.WriteLine(string.Format("{0} : {1}", jk.Group, jk.Name));
}
Console.WriteLine("ShowJobs : End");
Console.WriteLine("");
}
追加:
也许换一种方式。我稍微调整了一种方法。而是加了一个新的。
ReportIInterruptableJobs
查看 ReportIInterruptableJobs 报告的内容。
private static void ShowJobs(IScheduler sched, ILogger logger)
{
Console.WriteLine("");
Console.WriteLine("ShowJobs : Start");
GroupMatcher<JobKey> matcherAll = GroupMatcher<JobKey>.AnyGroup();
Quartz.Collection.ISet<JobKey> jobKeys = sched.GetJobKeys(matcherAll);
foreach (JobKey jk in jobKeys)
{
Console.WriteLine(string.Format("{0} : {1}", jk.Group, jk.Name));
IJobDetail jobData = sched.GetJobDetail(jk);
if (null != jobData)
{
Console.WriteLine(string.Format("{0}", jobData.JobType.AssemblyQualifiedName));
}
}
Console.WriteLine("ShowJobs : End");
Console.WriteLine("");
}
private static void ReportIInterruptableJobs()
{
Type typ = typeof(IInterruptableJob);
ICollection<Type> types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => typ.IsAssignableFrom(p)).ToList();
if (null != types)
{
foreach (Type t in types)
{
Console.WriteLine(string.Format("{0}", t.AssemblyQualifiedName));
}
}
}
您是否 100% 肯定 ListenerJob
的 JobKey
实际上是 Listeners.Realtime
?是否有其他工作使用 JobKey
?在您调用 Interrupt()
时,Listeners.Realtime
是否已更新为不同的值? Quartz 肯定会找到 JobKey
的工作,因为如果 Quartz 根本找不到工作,它不会抛出异常。它找到的工作没有实现 IInterruptableJob。我会通过在 ListenerJob
的 Execute(IJobExecutionContext context)
方法中设置断点并检查 context.JobDetail.Key
来仔细检查 ListenerJob
的 JobKey
值。我的钱在于 JobKey
与众不同。此外,查看您的 BaseJob
的代码可能对我们有所帮助。
我正在使用 Quartz 调度程序,并试图在关闭应用程序时关闭所有作业。我有一项专业工作是做 'Hold' 或 'Busy-wait',基本上直到它得到一个条件,它才会耐心地坐在那里等待。
由于新的集成点,这项工作是新的。该应用程序 运行 作为使用 Topshelf 的服务,每当我们尝试关闭该服务以对其进行升级时,既然这项工作是 运行ning,我们最终必须重新启动服务器才能使其正常运行关机。
无论如何,这里变得很奇怪,我只有一个作业类型,当我尝试使用作业 FireInstanceId
或 JobKey
:[=16 在以下代码部分中触发中断时=]
_logger.InfoFormat("{0} scheduler interrupting listener", scheduler.SchedulerName);
scheduler.Interrupt(ListenerKeys.Realtime);
_logger.InfoFormat("{0} scheduler shutting down", scheduler.SchedulerName);
scheduler.Shutdown(true);
_logger.InfoFormat("{0} scheduler shut down", scheduler.SchedulerName);
我遇到异常:
Job 'Listeners.Realtime' can not be interrupted, since it does not implement Quartz.IInterruptableJob
人们会认为这是直截了当的。但是,这是唯一使用此作业密钥的作业:
ListenerJob : BaseJob, IInterruptableJob
{
// some other code referenced in ExecuteJob
public void Interrupt()
{
_dequeuer.StopDequeing();
}
}
我会冒昧地说这就是你实现它的方式,所以我的问题变成了:Quartz 中是否存在已知错误?组密钥和中断是否存在问题?有没有办法告诉调度程序中断所有可中断的作业?有其他选择吗?
更新
我决定 运行 下面的代码,以便从下面的答案中进行更多诊断。 var interfaces 实际上包括 IInterruptableJob
var jobs = scheduler.GetCurrentlyExecutingJobs().Where(x => Equals(x.JobDetail.Key, ListenerKeys.Realtime));
var job1 = jobs.First();
var interfaces = job1.JobDetail.JobType.GetInterfaces();
此外,我 运行 ReportInterruptableJob 如下所示,它检查了程序集并确认 ListenerJob 实现了接口。
更新 2:
好的,我去了 git 中心,运行 确切的 meshos。 Job.JobInstance as IInterruptableInterface returns null,这就是我收到错误的原因。我想我不明白的是,我是如何围绕 IJo 形成 JobInstance 的,它确实实现了 IInterruptableJob
UPDATE3:好的....所以我在 bootstrap 中发现了一些正在使用 JobWrapper<> 的东西。我对此一无所知,但我确定这是其中的一部分。
所以我同意另一个答案(C骑士)的说法,JobKey可能已经关闭了。
如果您已经实现了接口...并且您拥有正确的 JobKey..那么您应该不会得到该异常。
下面是我用于中断工作的代码。我也在尝试找到 "findthekey" 逻辑。
private static void InterruptAJob(JobKey foundJobKey, IScheduler sched)
{
if (null != foundJobKey)
{
sched.Interrupt(foundJobKey);
}
}
追加
这是我用于查找工作密钥的代码。
我将从它开始............放置一些断点......并查看您的 JobKey 是否在集合中。也许修改例程(在你找出神奇的地方之后)以根据特定标准找到工作关键......如果你没有找到你期望找到的东西则抛出异常。 Aka,不要 "assume" JobKey 存在......但是查询它并抛出适当的异常(或者如果没有找到匹配则编写适当的 == null 逻辑)......vs "assume it has to be there" 方法。
同样,以下是 "starter" 代码......我的概念验证只有一项工作,所以我不必具体说明。
private static JobKey FindaJobKey(IScheduler sched, ILogger logger)
{
JobKey returnJobKey = null;
IList<string> jobGroupNames = sched.GetJobGroupNames();
if (null != jobGroupNames)
{
if (jobGroupNames.Count > 0)
{
GroupMatcher<JobKey> groupMatcher = GroupMatcher<JobKey>.GroupEquals(jobGroupNames.FirstOrDefault());
Quartz.Collection.ISet<JobKey> keys = sched.GetJobKeys(groupMatcher);
returnJobKey = keys.FirstOrDefault();
if (null == returnJobKey)
{
throw new ArgumentOutOfRangeException("No JobKey Found");
}
}
}
Thread.Sleep(TimeSpan.FromSeconds(1));
return returnJobKey;
}
追加:
也许是这样的:
private static JobKey FindJobKey(IScheduler sched, ILogger logger, string jobGroupName)
{
JobKey returnJobKey = null;
IList<string> jobGroupNames = sched.GetJobGroupNames();
if (null != jobGroupNames)
{
string matchingJobGroupName = jobGroupNames.Where(s => s.Equals(jobGroupName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (null != matchingJobGroupName)
{
GroupMatcher<JobKey> groupMatcher = GroupMatcher<JobKey>.GroupEquals(matchingJobGroupName);
Quartz.Collection.ISet<JobKey> keys = sched.GetJobKeys(groupMatcher);
if (null != keys)
{
if (keys.Count > 0)
{
throw new ArgumentOutOfRangeException(string.Format("More than one JobKey Found. (JobGroupName='{0}')", jobGroupName));
}
returnJobKey = keys.FirstOrDefault();
if (null != returnJobKey)
{
throw new ArgumentOutOfRangeException(string.Format("No JobKey Found. (JobGroupName='{0}')", jobGroupName));
}
}
}
}
Thread.Sleep(TimeSpan.FromSeconds(1));
return returnJobKey;
}
另一种快速而肮脏的 "look at what you got going on" 方法。
private static void ShowJobs(IScheduler sched)
{
Console.WriteLine("");
Console.WriteLine("ShowJobs : Start");
GroupMatcher<JobKey> matcherAll = GroupMatcher<JobKey>.AnyGroup();
Quartz.Collection.ISet<JobKey> jobKeys = sched.GetJobKeys(matcherAll);
foreach (JobKey jk in jobKeys)
{
Console.WriteLine(string.Format("{0} : {1}", jk.Group, jk.Name));
}
Console.WriteLine("ShowJobs : End");
Console.WriteLine("");
}
追加:
也许换一种方式。我稍微调整了一种方法。而是加了一个新的。 ReportIInterruptableJobs
查看 ReportIInterruptableJobs 报告的内容。
private static void ShowJobs(IScheduler sched, ILogger logger)
{
Console.WriteLine("");
Console.WriteLine("ShowJobs : Start");
GroupMatcher<JobKey> matcherAll = GroupMatcher<JobKey>.AnyGroup();
Quartz.Collection.ISet<JobKey> jobKeys = sched.GetJobKeys(matcherAll);
foreach (JobKey jk in jobKeys)
{
Console.WriteLine(string.Format("{0} : {1}", jk.Group, jk.Name));
IJobDetail jobData = sched.GetJobDetail(jk);
if (null != jobData)
{
Console.WriteLine(string.Format("{0}", jobData.JobType.AssemblyQualifiedName));
}
}
Console.WriteLine("ShowJobs : End");
Console.WriteLine("");
}
private static void ReportIInterruptableJobs()
{
Type typ = typeof(IInterruptableJob);
ICollection<Type> types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => typ.IsAssignableFrom(p)).ToList();
if (null != types)
{
foreach (Type t in types)
{
Console.WriteLine(string.Format("{0}", t.AssemblyQualifiedName));
}
}
}
您是否 100% 肯定 ListenerJob
的 JobKey
实际上是 Listeners.Realtime
?是否有其他工作使用 JobKey
?在您调用 Interrupt()
时,Listeners.Realtime
是否已更新为不同的值? Quartz 肯定会找到 JobKey
的工作,因为如果 Quartz 根本找不到工作,它不会抛出异常。它找到的工作没有实现 IInterruptableJob。我会通过在 ListenerJob
的 Execute(IJobExecutionContext context)
方法中设置断点并检查 context.JobDetail.Key
来仔细检查 ListenerJob
的 JobKey
值。我的钱在于 JobKey
与众不同。此外,查看您的 BaseJob
的代码可能对我们有所帮助。