Мульти-очереди
По умолчанию все задачи Jobby (в т.ч. и задачи по расписанию) извлекаются для запуска из одной единственной очереди. При наличии большого количества задач различных типов и высокой интенсивности их создания такая схема может оказаться неэффективной.
В качестве альтернативы Jobby позволяет определить несколько именованных очередей с различными настройками для обработки и распределять задачи между этими очередями.
Проблема единственной очереди
В чём конкретно могут проявляться проблемы, связанные с использованием одной единственной очереди?
Представим что у вас есть важная задача по расписанию и задачи, которые запускаются для однократного выполнения.
Допустим задача по расписанию должна запуститься в 00:00:00. Но в 23:59:55 в систему стало поступать огромное количество задач для однократного выполнения, которые планируются к запуску в период с 23:59:55 до 00:00:00.
Т.к. Jobby-сервер запускает задачи в порядке увеличения их запланированного времени запуска, он не запустит задачу по расписанию в 00:00:00, если он еще не запустил все задачи, запланированные к запуску в период с 23:59:55 до 00:00:00. А если их окажется достаточно много, задача по расписанию будет запущена только с очень большой задержкой.
Иными словами: большое количество задач одного типа может помешать своевременному запуску задач другого типа.
Другая проблема единственной очереди: задачи всех типов выполняются с одними и теми же ограничениями на степень параллелизма. К примеру для большинства типов задач вы можете себе позволить выполнять до 10 задач одновременно, поэтому вы установили настройку MaxDegreeOfParallelism в значение 10. Но в то же время у вас есть тип задач, которые очень сильно нагружают базу данных, и одновременный запуск 10 таких задач может привести к чрезмерной нагрузке и даже сбою. Такие задачи хотелось бы запускать не более чем по одной.
Все эти проблемы позволяет решить механизм мульти-очередей следующим образом:
- Для каждого Jobby-сервера определяется набор именованных очередей, которые он обслуживает
- Для каждой очереди можно указать своё ограничение на параллелизм
- Все создаваемые задачи можно распределять по разным очередям. Очередь может быть указана либо на уровне класса команды для всех задач конкретного типа, либо может быть указана при создании каждого отдельного экземпляра задачи.
- Каждый Jobby-сервер регулярно переключается между очередями, поэтому большое количество задач в одной очереди не будет блокировать своевременный запуск задач из другой очереди.
Настройка сервера
По умолчанию все задачи создаются в очереди под названием default и каждый Jobby-сервер обслуживает только эту очередь по умолчанию.
Чтобы использовать несколько очередей, их все (включая default) нужно указать в параметре JobbyServerSettings.Queues. У каждой очереди в этом списке доступны следующие параметры:
- QueueName - Название очереди. Обязательный параметр.
- MaxDegreeOfParallelism - Максимальная степень параллелизма при выполнении задач из данной очереди. Необязательный параметр, по умолчанию совпадает с
JobbyServerSettings.MaxDegreeOfParallelism. Допустимо переопределять это значение в меньшую сторону. - DisableSerializableGroups - Флаг при значении
falseотключающий в целях оптимизации для данной очереди механизм последовательного выполнения в группах (подробнее см. Последовательное исполнение). Значение по умолчанию -true. Важно: при использовании значенияfalseкорректная работа библиотеки возможно только при условии что все задачи очереди имеютSerializableGroupId=null.
Пример конфигурации:
builder.Services.AddJobbyServerAndClient(jobbyBuilder =>
{
jobbyBuilder.ConfigureJobby((sp, jobby) =>
{
jobby
.UseServerSettings(new JobbyServerSettings
{
MaxDegreeOfParallelism = 10,
Queues = [
// очередь по умолчанию
new QueueSettings
{
QueueName = "default"
},
// очередь для задач
// которые важно запускать вовремя
// например для рекуррентных
new QueueSettings
{
QueueName = "important"
},
// очередь для тяжёлых задач
// которым хотим уменьшить
// степень параллелизма
new QueueSettings
{
QueueName = "heavy",
MaxDegreeOfParallelism = 2
},
]
});
});
});Важно: если в настройках сервера не указать какую-либо очередь, то задачи созданные в этой очереди не будут запущены.
Выбор очереди для задачи
По умолчанию все задачи попадают в очередь default. Существует несколько способов переопределить очередь.
Во-первых, при настройке библиотеки можно указать очередь, в которую по умолчанию будут попадать все задачи по расписанию:
builder.Services.AddJobbyServerAndClient(jobbyBuilder =>
{
// Все рекуррентные задачи по умолчанию
// будут попадать в очередь important
// вместо очереди default
jobbyBuilder.UseQueueForAllRecurrent("important");
// ...
});Во-вторых, очередь по умолчанию можно указать в структурах JobOpts и RecurrentJobOpts на уровне класса команды, реализовав IHasDefaultJobOptions:
class MyHeavyCommand : IJobCommand, IHasDefaultJobOptions
{
public static string GetJobName() => "MyHeavyJob";
// Все обычные задачи для MyHeavyCommand
// будут создаваться в очереди heavy
public JobOpts GetOptionsForEnqueuedJob()
{
return new JobOpts
{
QueueName = "heavy"
}
}
// Все задачи по расписанию для MyHeavyCommand
// будут создаваться в очереди heavy
public RecurrentJobOpts GetOptionsForRecurrentJob()
{
return new RecurrentJobOpts()
{
QueueName = "heavy"
}
}
}Ну и в-третьих, очередь для задачи можно указать непосредственно при создании её экземпляра:
await jobbyClient.EnqueueCommandAsync(command, new JobOpts
{
QueueName = "heavy"
});
// аналогично для задач по расписанию
// и для методов jobbyClient.FactoryРекомендуемая конфигурация
Как мы рассмотрели в примере выше - использование одной единственной очереди может привести к нежелательным последствиям. Но и создание большого количества очередей тоже может негативно повлиять на производительность и увеличить на базу данных.
Поэтому рекомендуется использовать небольшое количество очередей, например:
- Очередь для задач, которые важно запускать вовремя (например для задач по расписанию).
- Очередь для тяжелых задач, для которых желательно снизить степень параллелизма.
- Очередь для задач, которым не нужна (или наоборот нужна) поддержка групп с последовательным исполнением (см. Последовательное исполнение).
- Очередь по умолчанию.