Sobes.tech
Назад к вопросам
Junior — Middle
63

Объясните концепцию Prefetch и как она применяется в RabbitMQ для управления доставкой сообщений

Компании, где спрашивали
Медиалогия

Ответ от нейросети

sobes.tech AI

Что хотят услышать интервьюеры:

Prefetch в RabbitMQ — это ограничение на количество сообщений, которые брокер может выдать потребителю без подтверждения. Это помогает контролировать нагрузку и не отправлять одному consumer слишком много сообщений сразу. На практике чаще всего используют prefetch вместе с manual ack, чтобы распределять сообщения более равномерно и избегать переполнения медленных потребителей.

Определение:

Prefetch задаёт размер «неподтверждённого окна» сообщений для consumer’а или канала: сколько сообщений RabbitMQ может доставить заранее, не дожидаясь ack по предыдущим. Если лимит достигнут, брокер перестаёт слать новые сообщения этому consumer’у, пока тот не подтвердит часть уже полученных.

Это не очередь в памяти приложения, а именно механизм flow control на стороне доставки сообщений.

Пример использования:

Типичный сценарий — есть несколько consumer’ов, но один из них обрабатывает тяжёлые задачи дольше других. Если не ограничить prefetch, RabbitMQ может отдать ему слишком много сообщений, и они будут «висеть» у медленного consumer’а, пока остальные простаивают.

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;

var factory = new ConnectionFactory { HostName = "localhost" };
using var connection = factory.CreateConnection();
using var channel = connection.CreateModel();

// Ограничиваем количество сообщений "в полёте"
channel.BasicQos(prefetchSize: 0, prefetchCount: 10, global: false);

var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
    var body = ea.Body.ToArray();
    var message = Encoding.UTF8.GetString(body);

    try
    {
        Console.WriteLine($"Processing: {message}");
        // обработка сообщения
        channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
    }
    catch
    {
        channel.BasicNack(deliveryTag: ea.DeliveryTag, multiple: false, requeue: true);
    }
};

channel.BasicConsume(queue: "task_queue", autoAck: false, consumer: consumer);
Console.ReadLine();

Пояснение кода:

Код показывает потребителя RabbitMQ на C# с ручным подтверждением сообщений. Сначала вызывается BasicQos(...), где prefetchCount: 10 означает, что consumer получит не больше 10 неподтверждённых сообщений одновременно. Параметр prefetchSize обычно оставляют 0, если ограничение по размеру в байтах не требуется. global: false означает, что лимит применяется к consumer, а не ко всем consumer’ам на канале.

Дальше consumer получает сообщение в обработчик Received, выполняет работу и вызывает BasicAck, если обработка успешна. Если произошла ошибка, используется BasicNack с requeue: true, чтобы сообщение можно было вернуть в очередь и обработать позже. Благодаря prefetch медленный consumer не накапливает слишком много задач, а RabbitMQ может лучше распределять сообщения между потребителями.

Ключевые моменты:

  • Prefetch ограничивает число сообщений, которые consumer может получить без ack.
  • Используется в связке с autoAck: false, иначе контроль доставки теряет смысл.
  • Помогает избежать ситуации, когда один consumer «забирает» слишком много сообщений и тормозит обработку.
  • Маленький prefetch улучшает справедливость распределения, но может снизить пропускную способность.
  • Большой prefetch повышает throughput, но увеличивает риск перекоса нагрузки и задержек.
  • В RabbitMQ BasicQos — основной способ настроить prefetch для потребителя.