问题
I'm working with Quartz scheduler, and attempting to close all jobs on shutdown of the app. I have one specialty job that does a 'Hold' or 'Busy-wait', basically until it gets a condition it sits there waiting patiently.
This job is new, due to a new integration point. The app is run as a service using Topshelf, and whenever we try to shutdown the service to upgrade it, now that this job is running, we have to end up restarting the server to get it to shutdown.
Anyhow, here it gets weird, I have a single jobtype, and when i try to trigger the interrupt in the following section of code using the jobs FireInstanceId
or JobKey
:
_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);
I get an exception:
Job 'Listeners.Realtime' can not be interrupted, since it does not implement Quartz.IInterruptableJob
One would assume this is straight forward. However Here is the ONLY job that uses this job key:
ListenerJob : BaseJob, IInterruptableJob
{
// some other code referenced in ExecuteJob
public void Interrupt()
{
_dequeuer.StopDequeing();
}
}
I'd go out on a limb and say that's how you implement it, so my question becomes: is there a known bug in Quartz? Is there an issue with group-keys and interrupts maybe? Is there just a way to tell the scheduler to interrupt ALL jobs that are interruptable? Is there an alternative?
UPDATE
I decided to run the following code for more diagnostics from below answers. var interfaces does in fact include IInterruptableJob
var jobs = scheduler.GetCurrentlyExecutingJobs().Where(x => Equals(x.JobDetail.Key, ListenerKeys.Realtime));
var job1 = jobs.First();
var interfaces = job1.JobDetail.JobType.GetInterfaces();
Additionally, I ran ReportInterruptableJob as suggested below, which checked the assembly and confirmed ListenerJob implements the interface.
UPDATE2:
Ok, I went out to git hub, and ran the exact meshos. Job.JobInstance as IInterruptableInterface returns null, which is why I get the error. What I don't understand I guess, I how the JobInstance is formed around the IJo which does implement IInterruptableJob
UPDATE3: Ok.... So I found something in the bootstrap that is using JobWrapper<>. I know nothing about it, but Im sure that is part of it.
回答1:
So I agree with the other Answer (C Knight) that the JobKey may be off.
If you've implemented the interface...and you have the right JobKey..then you should not get that exception.
Below is the code I have for the interrupting a job. I'm trying to find the "findthekey" logic as well.
private static void InterruptAJob(JobKey foundJobKey, IScheduler sched)
{
if (null != foundJobKey)
{
sched.Interrupt(foundJobKey);
}
}
APPEND
Here is my code for finding a job key.
I would start with it..........put some breakpoints...and see if your JobKey is in the collection. Maybe modify the routine (after you figure out the magic-places) to find a job key by a certain criteria..and throw an exception if you don't find what you're expecting to find. Aka, don't "assume" the JobKey exists.....but query for it and throw the appropriate exception (or write the appropriate == null logic if no match is found).......vs the "assume it has to be there" approach.
Again, the below is "starter" code.......my Proof of Concept only had one job in it, so I didn't have to be specific.
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;
}
APPEND:
Maybe something like this:
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;
}
Another quick and dirty "look at what you got going on" method.
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("");
}
APPEND:
Maybe come at it another way. I slightly adjusted one method. But added a new one. ReportIInterruptableJobs
See what ReportIInterruptableJobs reports.
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));
}
}
}
回答2:
Are you 100% positive that the JobKey
for your ListenerJob
is in fact Listeners.Realtime
? Could there be a different job that uses that JobKey
? Could Listeners.Realtime
be being updated to a different value by the time you call Interrupt()
? Quartz is definitely finding a job with that JobKey
, because Quartz does not throw an exception if it doesn't find a job at all. The job it IS finding does not implement IInterruptableJob. I would double check the JobKey
value for your ListenerJob
by setting a breakpoint in the Execute(IJobExecutionContext context)
method of ListenerJob
and inspecting the context.JobDetail.Key
. My money is on the JobKey
being different. Also, it might be helpful for us to see what the code for your BaseJob
is.
来源:https://stackoverflow.com/questions/32631573/trigging-interrupt-on-all-quartz-net-iinterruptablejob