Планирование заданий с высокой загрузкой процессора в Quartz.NET

#c# #wcf #c#-4.0 #scheduling #quartz.net

#c# #wcf #c #-4.0 #планирование #quartz.net

Вопрос:

Я использую Quartz.NET запланировать задание, которое загружает кучу данных из внешних источников и сохраняется в базе данных. После их загрузки на них также должна выполняться некоторая обработка, которая создаст дополнительные записи в другой таблице в базе данных.

Задание загрузчика является заданием с отслеживанием состояния и выполняется каждую минуту. Проблема, с которой я столкнулся, заключается в том, что после загрузки данных обработка их части может занять гораздо больше времени, чем я ожидал.

Как мне это сделать? Я подумал о создании другого задания (которое будет выполняться только один раз), когда завершится загрузка данных. В этом случае задание загрузчика может выполняться каждую минуту (таков был первоначальный план), потому что часть загрузки занимает всего 5-20 секунд, а другое задание может обрабатывать эти записи после завершения загрузчика. Задание обработчика будет извлекать записи, помеченные как необработанные, из базы данных и выполнять с ними работу.

Является ли это правильным подходом для обработки обработки? Другая идея, к которой я пришел, — настроить службу WCF, которая будет обрабатывать один загруженный элемент. Это будет вызываться для каждого загруженного элемента. Однако я не думаю, что это будет работать лучше, чем другой подход к работе.

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

1. processing part Связана ли операция с ЦП или она также включает в себя тяжелые операции ввода-вывода?

2. Под высокой нагрузкой я имел в виду загрузку процессора, поэтому в процессе нет интенсивного ввода-вывода.

3. Хорошо, так что можете ли вы попробовать выполнить эту обработку параллельно, используя TPL, и посмотреть, получите ли вы что-нибудь, чтобы это было сделано в приемлемое время

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

Ответ №1:

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

Задание просто отправит сообщение, определяющее намерение, другой конечной точке.

Это очень распространено в архитектуре, управляемой событиями, с каким-то посредником сообщений или служебной шиной, такой как NServiceBus или MassTransit.

Таким образом, ваш планировщик будет оставаться экономичным и масштабируемым.

Вот пример:

 public class DownloadFileJob : IJob
{
    public IBus Bus { get; set; }
    public ILogger Logger{ get; set; }

    public void Execute(IJobExecutionContext context)
    {
        Bus.Send(new DownloadFileMessage());
        Logger.Info("Sending message requesting download of file.");
    }
}
  

Ответ №2:

Ваш подход к тому, чтобы задание загрузки планировало другое задание для выполнения обработки, выполним и широко используется.

Однако следует иметь в виду, что это работает до тех пор, пока задание загрузки не всегда планирует задание обработки. Если ваше задание загрузки всегда планирует задание (скажем, каждую минуту), а выполнение запланированного задания занимает больше времени, чем это требуется, у вас в конечном итоге закончатся потоки обработки, и вашему заданию загрузки придется ждать, пока поток будет доступен.

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

1. Хм, я не думал о возможной проблеме, о которой вы упомянули. Существует ли какая-либо «рекомендуемая» стратегия или метод для предотвращения этой ситуации?

2. Вы могли бы запускать длительные задания в другом планировщике, возможно, с достаточной емкостью, чтобы у вас не заканчивались потоки.

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

4. Приоритет используется только для разрыва связей, когда 2 триггера должны срабатывать одновременно, так что это не решит проблему. Quartz.net выполняет задания в порядке, основанном на времени следующего срабатывания триггера.

Ответ №3:

Вы можете изучить настройку a JobListener для задания загрузки. Просто создайте класс, который реализует IJobListener интерфейс, затем поместите свой код обработки в JobWasExecuted метод:

 public PostDownloadJobListener : IJobListener
{
    string Name { get { return "MyJobListener"; } }
    void JobToBeExecuted(JobExecutionContext context) { }
    void JobExecutionVetoed(JobExecutionContext context) { }
    void JobWasExecuted(JobExecutionContext context, JobExecutionException jobException)
    {
        // Perform processing here
    }
}
  

Зарегистрируйте прослушиватель с помощью scheduler.AddJobListener(myJobListener); , и пусть прослушиватель выполняет обработку после успешного выполнения задания.