在所有 Quartz .NET IInterruptableJob 上触发中断

Trigging Interrupt on all Quartz .NET IInterruptableJob

我正在使用 Quartz 调度程序,并试图在关闭应用程序时关闭所有作业。我有一项专业工作是做 'Hold' 或 'Busy-wait',基本上直到它得到一个条件,它才会耐心地坐在那里等待。

由于新的集成点,这项工作是新的。该应用程序 运行 作为使用 Topshelf 的服务,每当我们尝试关闭该服务以对其进行升级时,既然这项工作是 运行ning,我们最终必须重新启动服务器才能使其正常运行关机。

无论如何,这里变得很奇怪,我只有一个作业类型,当我尝试使用作业 FireInstanceIdJobKey:[=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% 肯定 ListenerJobJobKey 实际上是 Listeners.Realtime?是否有其他工作使用 JobKey?在您调用 Interrupt() 时,Listeners.Realtime 是否已更新为不同的值? Quartz 肯定会找到 JobKey 的工作,因为如果 Quartz 根本找不到工作,它不会抛出异常。它找到的工作没有实现 IInterruptableJob。我会通过在 ListenerJobExecute(IJobExecutionContext context) 方法中设置断点并检查 context.JobDetail.Key 来仔细检查 ListenerJobJobKey 值。我的钱在于 JobKey 与众不同。此外,查看您的 BaseJob 的代码可能对我们有所帮助。