В Quartz.NET есть ли способ установить свойство, которое позволит запускать только один экземпляр задания?

#.net #quartz.net

#.net #quartz.net

Вопрос:

У меня есть служба, которая будет запускаться каждые X минут. Если это задание по какой-либо непредвиденной причине занимает больше X минут, я хочу убедиться, что триггер не запускает второй экземпляр этого задания.

Пример сценария

  • У меня есть задание X, оно собирает файлы и запускается Quartz каждые 1 минуту.
  • Задание X обычно может обрабатывать 100 файлов за 1 минуту, все, что превышает 100 файлов, займет больше 1 минуты.
  • С момента последнего запуска там оказалось 150 файлов, поэтому задание X запускается и начинает обработку. По истечении 1 минуты было обработано 100 файлов, осталось 50 файлов, и задание X продолжает выполняться.
  • Однако запускается второй экземпляр задания X, поскольку триггер срабатывает каждые 1 минуту.
  • Теперь у меня есть 2 экземпляра задания X, собирающих те же 50 файлов.

Есть ли способ подключить Quartz.NET разрешить только 1 экземпляр задания? Я согласен со вторым триггером, ожидающим завершения первого, или я также согласен с возможностью пропустить второй триггер, поскольку он будет запущен снова через минуту.

Я взглянул на Java-версию Quartz API и нашел свойство ‘DisallowConcurrentExecution‘, но не нашел ничего подобного в .Сетевая версия.

Мой код для Quartz.Сетевая реализация

общедоступный планировщик IScheduler { get; set; }
общедоступный IJobListener AutofacJobListener { get; set; }

public void Start()
{
var trigger = TriggerUtils.MakeMinutelyTrigger(1);
триггер.Name = @"Триггер импорта документа";

Планировщик.ScheduleJob(new JobDetail("Импорт документа", null, typeof(DocumentImportJob)), триггер);
Планировщик.Добавьте глобальный JobListener (AutofacJobListener);
Планировщик.Start();
}

Комментарии:

1. Что должно произойти, если расписание снова «переключится», в то время как предыдущее все еще выполняется? Отложить или пропустить?

2. Пропустить или отложить, я открыт для любого варианта.

3. Разве вы не можете просто установить переменную или что-то еще во время ее выполнения? ie. пусть задание «запустится», оно просто немедленно завершится.

4. Реализация логики внутри вашей функции должна быть относительно простой.

5. Я попробовал, как вы предложили, и пометил как ответ.. Но я не могу решить проблему. Пожалуйста, проверьте проблему в github. Что мне следует сделать, чтобы решить проблему? github.com/quartznet/quartznet/issues/469

Ответ №1:

Используйте атрибут DisallowConcurrentExecution.

Объявите свой класс следующим образом:

 [DisallowConcurrentExecution]
public class SomeTask : IJob
{

} 
  

Осечки

«Инструкции по пропуску Другим важным свойством триггера является его «инструкция по пропуску». Ошибка срабатывания возникает, если постоянный триггер «пропускает» время запуска из-за завершения работы планировщика или из-за отсутствия доступных потоков в Quartz.NET Пул потоков для выполнения задания. Для разных типов триггеров доступны разные инструкции по пропуску. По умолчанию они используют инструкцию ‘smart policy’, которая имеет динамическое поведение, основанное на типе триггера и конфигурации. При запуске планировщика он выполняет поиск любых постоянных триггеров, которые дали сбой, и затем обновляет каждый из них на основе их индивидуально настроенных инструкций по пропуску. Когда вы начинаете использовать Quartz.NET в ваших собственных проектах вы должны ознакомиться с инструкциями misfire, которые определены для данных типов триггеров и объяснены в их документации API. Более конкретная информация об инструкциях по пропуску будет дана в уроках руководства, специфичных для каждого типа триггера.»

Ознакомьтесь с информацией «инструкции по запуску сбоя» внизу этих страниц:

Урок 5: SimpleTrigger

Урок 6: CronTrigger

Старый Quartz.NET Ответ API:

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

Экземпляры IStatefulJob следуют немного иным правилам, чем обычные экземпляры IJob. Ключевое отличие заключается в том, что их связанная карта данных JobDataMap повторно сохраняется после каждого выполнения задания, таким образом сохраняя состояние для следующего выполнения. Другое отличие заключается в том, что заданиям с отслеживанием состояния не разрешается выполняться одновременно, что означает новые триггеры, которые возникают до завершения IJob.Выполнение метода будет отложено.

Итак, объявите свой класс ‘Job’ следующим образом:

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

Чтобы избежать повторного запуска отложенных задач сразу после завершения задания (когда задание занимает более 1 минуты и вызывает «осечку» триггера), при создании триггера (ов) выполните следующие действия (настройте в зависимости от используемого типа триггера):

 myJobTrigger.MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;  
  

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

Комментарии:

1. IStatefulJob устарел. Вместо этого используйте [DisallowConcurrentExecution] над определением класса задания. Это согласно Quartz.NET 2.2.1. И сбой может быть обработан следующим методом, используемым при инициализации триггера: С помощью misfirehandlinginstructionignoremisfires()

Ответ №2:

В качестве обновления к этому ответу в более новых версиях Quartz.Net теперь это делается с помощью атрибута «DisallowConcurrentExecution», который вы применяете к своей реализации задания:

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

И для инструкции misfire, вот как это сделать:

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

Ответ №3:

Что ж, одним из простых способов сделать это было бы просто сохранить значение флага где-нибудь в переменной и проверять переменную при входе в метод задания.

Таким образом, вы бы просто позволили заданию «запуститься» во второй раз, оно бы просто немедленно завершилось, не выполняя никакой реальной работы.

Вот пример:

 private volatile bool _IsRunning;

...

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

Комментарии:

1. Ваш ответ великолепен, но я должен согласиться с @Keith’s, потому что это именно то, что я искал, и давайте я позволю Quartz обрабатывать все.

2. @Mark Да, я также ввел комментарий на этот счет (до того, как он опубликовал свой ответ), что вам следует повременить с принятием моего ответа, потому что в Quartz может быть что-то. Net это сделало бы именно то, что вы хотели, но я почему-то пропустил нажатие кнопки «Добавить комментарий». Должно быть, я сам, жаждущий репутации, отказался от этой части 🙂

Ответ №4:

общедоступный валидатор bool (контекст IJobExecutionContext) {

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

    }