在 Quartz.NET 中,有没有一种方法可以设置一个属性,只允许一个作业实例运行?

发布于 2024-11-06 11:31:40 字数 1207 浏览 3 评论 0原文

我有一项服务每 X 分钟运行一次。如果由于某种不可预见的原因该作业花费的时间超过 X 分钟,我想确保触发器不会启动该作业的第二个实例。

示例场景

  • 我有作业 X,拾取文件并由 Quartz 每 1 分钟触发一次。
  • 作业 X 通常可以在 1 分钟内处理 100 个文件,超过 100 个文件将花费超过 1 分钟的时间。
  • 自上次运行以来,恰好有 150 个文件存在,因此作业 X 启动并开始处理。当达到 1 分钟时,已处理 100 个文件,剩余 50 个文件,作业 X 继续运行。
  • 然而,作业 X 的第二个实例已启动,因为触发器每 1 分钟触发一次。
  • 我现在有 2 个 Job X 实例拾取相同的 50 个文件。

有没有办法连接 Quartz.NET 以仅允许一个作业实例?我可以接受第二个触发器等待第一个触发器完成,或者我也可以选择跳过第二个触发器,因为它会在一分钟内再次触发。



我查看了 Quartz API 的 Java 版本,发现了一个属性 'DisallowConcurrentExecution',但在.NET版本中没有找到类似的。

我的 Quartz.NET 实现代码

public IScheduler Scheduler { get; set; }
public IJobListener AutofacJobListener { get; set; }

public void Start()
{
var trigger = TriggerUtils.MakeMinutelyTrigger(1);
trigger.Name = @"Document Import Trigger";

Scheduler.ScheduleJob(new JobDetail("Document Import", null, typeof(DocumentImportJob)), trigger);
Scheduler.AddGlobalJobListener(AutofacJobListener);
Scheduler.Start();
}

I have a service that will run every X minutes. If that job takes longer than X minutes for some unforeseen reason I want to make sure that the trigger doesn't kick off a second instance of this job.

Sample Scenario

  • I have Job X, picks up files and is triggered by Quartz every 1 minute.
  • Job X can typically process 100 files in 1 minute, anything over 100 files will take longer than 1 minute.
  • Since the last run time, 150 files happen to be out there so Job X kicks off and begins processing. When 1 minute is reached 100 files were processed, 50 files remain, and Job X continues to run.
  • However a second instance of Job X is kicked off because the trigger fires every 1 minute.
  • I now have 2 instances of Job X picking up the same 50 files.

Is there a way to wire up Quartz.NET to only allow 1 instance of a Job? I'm OK with the second trigger waiting for the first to complete or I'm also OK with an option for it to skip the second trigger since it will be triggered again in a minute.

I took a look at the Java version of Quartz API and found a property 'DisallowConcurrentExecution' but didn't find one similar in the .NET version.

My Code for the Quartz.NET Implementation

public IScheduler Scheduler { get; set; }
public IJobListener AutofacJobListener { get; set; }

public void Start()
{
var trigger = TriggerUtils.MakeMinutelyTrigger(1);
trigger.Name = @"Document Import Trigger";

Scheduler.ScheduleJob(new JobDetail("Document Import", null, typeof(DocumentImportJob)), trigger);
Scheduler.AddGlobalJobListener(AutofacJobListener);
Scheduler.Start();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夜声 2024-11-13 11:31:40

使用 DisallowConcurrentExecution 属性。

按如下方式声明您的类:

[DisallowConcurrentExecution]
public class SomeTask : IJob
{

} 

Misfires

“失火指令触发器的另一个重要属性是它的
“失火指令”。如果持续触发,就会发生失火
由于调度程序被关闭而“错过”其触发时间,或者
因为 Quartz.NET 的线程池中没有可用的线程
执行作业。不同的扳机类型有不同的失火情况
他们可以使用的说明。默认情况下,他们使用“智能策略”
指令 - 具有基于触发类型的动态行为和
配置。当调度程序启动时,它会搜索任何
已失败的持久触发器,然后更新每个
它们基于各自配置的失火指令。什么时候
当你开始在自己的项目中使用 Quartz.NET 时,你应该
您自己熟悉定义的失火指令
给定的触发器类型,并在其 API 文档中进行了解释。
将给出有关失火指令的更多具体信息
在针对每种触发器类型的教程课程中。”

查看这些页面底部的“触发失火说明”信息:

第 5 课:SimpleTrigger

第 6 课:CronTrigger

旧 Quartz.NET API 答案:

http://quartznet.sourceforge.net/apidoc/topic142.html

IStatefulJob 实例遵循的规则与常规实例略有不同
IJob 实例。关键的区别是
他们关联的 JobDataMap 是
每次执行后重新保留
工作,从而保留状态
下一次执行。另一个区别
不允许有状态作业
并发执行
,这意味着
之前发生的新触发器
IJob.Execute 方法完成
将会延迟。

因此,请按如下方式声明您的“作业”类:

class DocumentImportJob : IStatefulJob
{
   ......
} 

为了避免作业完成后立即重新触发延迟任务(当作业花费超过 1 分钟并导致触发器“失火”时),请在创建触发器时执行以下操作(根据使用的触发器类型进行调整):

myJobTrigger.MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;  

https://www.quartz-scheduler.net/documentation/quartz-2.x/tutorial/more-about-triggers.html

Use the DisallowConcurrentExecution attribute.

Declare your class as follows:

[DisallowConcurrentExecution]
public class SomeTask : IJob
{

} 

Misfires

"Misfire Instructions Another important property of a Trigger is its
"misfire instruction". A misfire occurs if a persistent trigger
"misses" its firing time because of the scheduler being shutdown, or
because there are no available threads in Quartz.NET's thread pool for
executing the job. The different trigger types have different misfire
instructions available to them. By default they use a 'smart policy'
instruction - which has dynamic behavior based on trigger type and
configuration. When the scheduler starts, it searches for any
persistent triggers that have misfired, and it then updates each of
them based on their individually configured misfire instructions. When
you start using Quartz.NET in your own projects, you should make
yourself familiar with the misfire instructions that are defined on
the given trigger types, and explained in their API documentation.
More specific information about misfire instructions will be given
within the tutorial lessons specific to each trigger type."

Check out the "trigger misfire instructions" information at the bottom of these pages:

Lesson 5: SimpleTrigger

Lesson 6: CronTrigger

Old Quartz.NET API answer:

http://quartznet.sourceforge.net/apidoc/topic142.html:

IStatefulJob instances follow slightly different rules from regular
IJob instances. The key difference is
that their associated JobDataMap is
re-persisted after every execution of
the job, thus preserving state for the
next execution. The other difference
is that stateful jobs are not allowed
to Execute concurrently
, which means
new triggers that occur before the
completion of the IJob.Execute method
will be delayed.

So, declare your 'Job' class as follows:

class DocumentImportJob : IStatefulJob
{
   ......
} 

To avoid delayed tasks re-firing immediately after job completes (when the job takes more than 1 minute and causes a trigger 'misfire'), do the following when creating your trigger(s) (adjust depending on the trigger type used):

myJobTrigger.MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;  

https://www.quartz-scheduler.net/documentation/quartz-2.x/tutorial/more-about-triggers.html

伏妖词 2024-11-13 11:31:40

作为此答案的更新,在较新版本的 Quartz.Net 中,现在通过应用于作业实现的属性“DisallowConcurrentExecution”来完成:

[DisallowConcurrentExecution]
public class MyJob : IJob  
{
    ..
}

对于失火指令,以下是如何做到这一点:

var trigger = TriggerBuilder.Create()
    .WithSimpleSchedule(ssb => ssb.WithIntervalInMinutes(interval)
        .RepeatForever()
        .WithMisfireHandlingInstructionIgnoreMisfires()
    )
    .Build();

As an update to this answer, in newer versions of Quartz.Net this is now done via an attribute "DisallowConcurrentExecution" you apply to your job implementation:

[DisallowConcurrentExecution]
public class MyJob : IJob  
{
    ..
}

And for the misfire instruction, here is how to do that:

var trigger = TriggerBuilder.Create()
    .WithSimpleSchedule(ssb => ssb.WithIntervalInMinutes(interval)
        .RepeatForever()
        .WithMisfireHandlingInstructionIgnoreMisfires()
    )
    .Build();
娇妻 2024-11-13 11:31:40

好吧,一种简单的方法是将标志值存储在某个变量中,并在进入作业方法时检查该变量。

这样你就可以让工作第二次“开始”,它会立即退出而不做任何实际的工作。

这是一个例子:

private volatile bool _IsRunning;

...

if (Interlocked.Exchange(ref _IsRunning, true))
    return;
try
{
    // job code
}
finally
{
    _IsRunning = false;
}

Well, one simple way to do it would be to just store a flag value in a variable somewhere, and check the variable upon entrance to the job method.

That way you would just let the job "start" a second time, it would just exit immediately without doing any real work.

Here's an example:

private volatile bool _IsRunning;

...

if (Interlocked.Exchange(ref _IsRunning, true))
    return;
try
{
    // job code
}
finally
{
    _IsRunning = false;
}
日久见人心 2024-11-13 11:31:40

公共 bool Validar(IJobExecutionContext 上下文)
{

        if (context.Scheduler.GetCurrentlyExecutingJobs().Any(x => x.FireInstanceId != context.FireInstanceId
         && x.JobDetail.Key == context.JobDetail.Key))
        {
            return true;
        }
        else
        {
            return false;
        }

    }

public bool Validar(IJobExecutionContext context)
{

        if (context.Scheduler.GetCurrentlyExecutingJobs().Any(x => x.FireInstanceId != context.FireInstanceId
         && x.JobDetail.Key == context.JobDetail.Key))
        {
            return true;
        }
        else
        {
            return false;
        }

    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文