【问题标题】:Trigging Interrupt on all Quartz .NET IInterruptableJob在所有 Quartz .NET IInterruptableJob 上触发中断
【发布时间】:2015-09-17 13:29:26
【问题描述】:

我正在使用 Quartz 调度程序,并尝试在关闭应用程序时关闭所有作业。我有一项专门的工作是“等待”或“忙碌等待”,基本上直到它得到一个条件,它就坐在那里耐心地等待。

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

无论如何,这很奇怪,我只有一个作业类型,当我尝试使用作业FireInstanceIdJobKey 触发以下代码部分中的中断时:

_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' 不能被中断,因为它没有实现 Quartz.IInterruptableJob

人们会认为这是直截了当的。但是,这是唯一使用此作业密钥的作业:

ListenerJob : BaseJob, IInterruptableJob
{
    // some other code referenced in ExecuteJob
    public void Interrupt()
    {
        _dequeuer.StopDequeing();
    }
}

我会冒险说这就是你实现它的方式,所以我的问题变成了:Quartz 中是否存在已知的错误?组键和中断是否存在问题?有没有办法告诉调度程序中断所有可中断的作业?有其他选择吗?

更新

我决定运行以下代码以从以下答案中进行更多诊断。 var 接口实际上包含 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 hub,并运行了确切的 meshos。 Job.JobInstance 作为 IInterruptableInterface 返回 null,这就是我收到错误的原因。我想我不明白的是,JobInstance 是如何围绕实现 IInterruptableJob 的 IJo 形成的

UPDATE3:好的....所以我在引导程序中发现了一些使用 JobWrapper 的东西。我对此一无所知,但我确信这是其中的一部分。

【问题讨论】:

  • 我曾经有过类似的问题:stackoverflow.com/questions/21537460/…
  • 您能否添加某种日志记录机制来查看您的 ListenerJob.Interrupt 方法是否被调用?您可以将其包装在 try catch 中(并登录 catch)以确保方法内的代码不会乱码吗?
  • @granadaCoder 我知道它没有被击中,因为在尝试调用该方法时会引发异常。另外,我在那里留下了一个断点,只是为了仔细检查我的理论并确认它没有被击中。
  • 我附加了我的答案............基本上,我发布了一些代码,以便您可以更好地钓鱼以确保假定的 JobKey 确实存在。

标签: c# quartz-scheduler quartz.net


【解决方案1】:

所以我同意 JobKey 可能已关闭的另一个答案(C Knight)。

如果你已经实现了接口......并且你有正确的 JobKey..那么你不应该得到那个异常。

以下是我用于中断工作的代码。我也在尝试找到“findthekey”的逻辑。

 private static void InterruptAJob(JobKey foundJobKey, IScheduler sched)
    {
        if (null != foundJobKey)
        {
            sched.Interrupt(foundJobKey);
        }
    }

追加

这是我查找工作密钥的代码。

我会从它开始............放一些断点......然后看看你的 JobKey 是否在集合中。也许修改例程(在你找出神奇的地方之后)以按照特定标准找到工作键......如果你没有找到你期望找到的东西,则抛出异常。 Aka,不要“假设” JobKey 存在......但是查询它并抛出适当的异常(或者如果没有找到匹配项,则编写适当的 == null 逻辑).......vs “假设它必须存在”的方法。

再一次,下面是“入门”代码.......我的概念证明只有一项工作,所以我不必具体说明。

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;
}

另一种快速而肮脏的“看看你在做什么”的方法。

    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));
            }
        }

    }

【讨论】:

  • 我使用了你的方法,它确实返回了 Listeners.Realtime,即使我关闭了所有其他工作启动和初始化。
  • 对,所以使用你拥有的 JobKey (Listeners.Realtime),它是否指向 ListenerJob (class/assembly)? (。使用上面的代码片段来收集线索)
  • ReportInterruptableJobs 确认我的 listenerJob 实现了接口。
  • 这就是我们两个。所以我去 github 看了一下东西,我应该可以将作业实例转换为 IInterruptable 作业,但我不能。
  • 我想确保你没有多个组装......代表 IInteruptable 工作......Aka,你正在使用一个文件来构建你的工作..然后另一个(同名程序集,但只是“不同”)......也就是它们看起来像相同的界面,但由于程序集不同,可能并不完全相同。
【解决方案2】:

您是否 100% 肯定您的 JobKeyListenerJob 实际上是 Listeners.Realtime?是否有其他工作使用该JobKey?在您调用 Interrupt() 时,Listeners.Realtime 是否会更新为不同的值? Quartz 肯定会通过JobKey 找到工作,因为如果 Quartz 根本找不到工作,它不会抛出异常。它正在寻找的工作没有实现 IInterruptableJob。我会通过在ListenerJobExecute(IJobExecutionContext context) 方法中设置断点并检查context.JobDetail.Key 来仔细检查ListenerJobJobKey 值。我的钱是 JobKey 与众不同。此外,看看您的BaseJob 的代码是什么,可能对我们有所帮助。

【讨论】:

  • Chris:II 按照下面的建议确定它是正确的键,而且看起来确实是,
  • 我在 Interrupt() 的源代码中看到了这样的注释:“如果当前正在执行多个已识别作业的实例,则将在每个实例上调用该方法。但是,有一个限制在一个实例抛出异常的情况下,所有剩余的实例(尚未被中断的)都不会调用它们的方法。”我建议运行 scheduler.GetCurrentlyExecutingJobs().Where(x => Equals(x.JobDetail.Key, ListenerKeys.Realtime));并查看是否返回了多个作业以及是否所有返回的作业都实现了 IInterruptableJob
  • 优秀的方法:它返回了两个ListenerJob(这是正确的)作业实例,这两个实例都实现了IInterruptableInterface。我在上面附加了更多信息
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-25
  • 1970-01-01
  • 1970-01-01
  • 2014-11-05
  • 1970-01-01
  • 2022-11-07
  • 1970-01-01
相关资源
最近更新 更多