#.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. Более конкретная информация об инструкциях по пропуску будет дана в уроках руководства, специфичных для каждого типа триггера.»
Ознакомьтесь с информацией «инструкции по запуску сбоя» внизу этих страниц:
Старый 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;
}
}