Устранение медленной обработки сообщений: выявление узких мест RabbitMQ
Диагностика замедлений RabbitMQ путем разделения узких мест: производитель, брокер, очередь, потребитель, диск и подтверждения.
Устранение медленной обработки сообщений: выявление узких мест RabbitMQ
Когда очередь RabbitMQ переполняется, очередь лишь показывает симптом. Узким местом может быть медленный потребитель, заблокированный издатель, дисковая тревога, неправильное значение prefetch, огромный размер сообщения или база данных, которая незаметно начала выдавать тайм-ауты. Перезапуск RabbitMQ может очистить график на несколько минут, но редко устраняет причину медленной обработки сообщений.
Самый быстрый путь диагностики — разделить поток на части: публикация в RabbitMQ, маршрутизация в очереди, хранение сообщений, доставка потребителям, обработка работы и подтверждение завершения. Каждая часть оставляет разные следы.
Первое разделение: готовые или неподтвержденные
Начните с счетчиков очереди:
rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers state
messages_ready означает, что сообщения находятся в очереди и ожидают доставки. Если это число растет, у RabbitMQ либо нет доступных потребителей, либо потребители достигли лимита prefetch, либо доставка блокируется другим условием.
messages_unacknowledged означает, что сообщения уже доставлены потребителям, и RabbitMQ ожидает ack, nack, reject или закрытия канала. Если это число растет, узкое место обычно находится внутри потребителя или в том, что вызывает потребитель.
Это различие важно. Если готовых сообщений много, а неподтвержденных мало, добавление памяти брокера не заставит потребителей появиться. Если неподтвержденных много, добавление разделов очереди может не помочь, так как работа уже покинула очередь.
Проверьте, действительно ли потребители присутствуют
Удивительное количество инцидентов «RabbitMQ медленный» на самом деле являются инцидентами «потребители не работают». Развертывание не удалось, автоскейлинг обнулился, изменились учетные данные, использовался неправильный виртуальный хост, или сервис подключен к стейджингу, а издатели публикуют в продакшн.
Используйте:
rabbitmqctl list_consumers queue_name channel_pid consumer_tag ack_required prefetch_count active
Если потребителей нет, сначала исправьте это. Если потребители присутствуют, но неактивны, проверьте логи приложения и состояние соединения. Если у каждого потребителя prefetch равен 1, а каждое сообщение обрабатывается несколько секунд, низкая параллельность доставки может быть ожидаемой. Если у каждого потребителя prefetch равен 500, а неподтвержденных сообщений огромное количество, потребители могут накапливать работу, которую не могут быстро завершить.
Измерьте время обработки потребителя
RabbitMQ может сообщить вам, что сообщения не подтверждены. Он не может сказать, разбирает ли потребитель огромный payload, ожидает PostgreSQL, повторяет HTTP-вызов или застрял на блокировке.
Добавьте замеры вокруг реального обработчика:
message_received_at
decode_ms
business_logic_ms
database_ms
external_api_ms
ack_ms
message_completed_at
Вам не нужна идеальная система трассировки, чтобы узнать что-то. Даже несколько структурированных полей лога могут показать, что обработчик обычно занимает 80 мс, но теперь тратит 4 секунды в ожидании внешнего API.
Если работа медленная, но параллелизуемая, добавьте экземпляры потребителей или увеличьте внутреннюю параллельность воркеров. Если ограничением является внешняя система, добавление потребителей может ухудшить ситуацию. Возможно, вам понадобится ограничение скорости, пакетирование, кэширование или отдельная очередь повторных попыток.
Настройте prefetch после понимания обработчика
Prefetch контролирует, сколько неподтвержденных сообщений RabbitMQ может отправить каждому потребителю. Он часто участвует в инцидентах медленной обработки, потому что изменяет видимость отставания.
При низком prefetch сообщения остаются готовыми в RabbitMQ, пока потребитель не будет готов к новым. Это справедливо и легко наблюдать, но может недоиспользовать очень быстрых потребителей.
При высоком prefetch сообщения быстро перемещаются к потребителям. Это может увеличить пропускную способность для быстрых обработчиков, но также может скрыть задержку. Медленный потребитель с большим значением prefetch может сидеть на сотнях сообщений, в то время как другие потребители остаются без работы.
Практический шаг при инциденте — уменьшить prefetch для медленных или нестабильных потребителей и наблюдать, улучшится ли хвостовая задержка. Для быстрых потребителей с низкой загрузкой ЦП и высоким количеством готовых сообщений осторожно увеличьте prefetch и измерьте снова.
Ищите узкие места на стороне издателя
Иногда очередь переполняется не из-за медленных потребителей. Она переполняется, потому что издатели публикуют сообщения пачками, а затем неэффективно ожидают подтверждений.
Подтверждения издателя — правильный инструмент, когда издателям нужно знать, что RabbitMQ принял сообщения. Медленный паттерн — ожидание каждого подтверждения перед публикацией следующего сообщения. Это превращает каждую публикацию в круговой обход.
Лучшие паттерны используют асинхронные подтверждения или ограниченные пакеты. Издатель может держать ограниченное количество сообщений в полете, обрабатывать nacks и все равно избегать блокировки на каждом отдельном сообщении. Лимит важен. Неограниченная публикация в полете может переместить узкое место в память издателя или нагрузку на брокер.
Проверьте метрики издателя: скорость публикации, задержку подтверждения, количество подтверждений в полете, переподключения, возвращенные сообщения и исключения каналов. В интерфейсе управления сравните скорость публикации со скоростью доставки/подтверждения. Если скорость публикации низкая, хотя приложение занято, производитель может ожидать подтверждений, транзакций или смены соединений.
Избегайте транзакций AMQP для высокопроизводительной публикации, если нет особой причины. Они гораздо дороже, чем подтверждения издателя для типичной надежной публикации.
Следите за диском, прежде чем винить RabbitMQ
Постоянные сообщения, очереди кворума, стримы и большие отставания включают диск. Когда задержка диска возрастает, поток сообщений может значительно замедлиться.
На узле RabbitMQ проверьте:
rabbitmq-diagnostics status
rabbitmq-diagnostics alarms
rabbitmq-diagnostics memory_breakdown
На уровне ОС используйте такие инструменты, как iostat, vmstat или графики мониторинга облака. Смотрите на задержку диска и I/O wait, а не только на пропускную способность. Облачный диск, исчерпавший кредиты burst, может выглядеть нормально в конфигурации, но ужасно на практике.
Если диск является узким местом, возможные исправления включают более быстрое хранилище, меньшее количество постоянных записей, меньшие сообщения, лучшее пакетирование подтверждений издателя, разделение очередей или перемещение рабочих нагрузок типа replay в стримы или другую лог-ориентированную систему. Не отключайте постоянство для сообщений, которые бизнес не может потерять, только чтобы сделать график зеленым.
Проверьте тревоги и заблокированные соединения
RabbitMQ защищает себя с помощью тревог памяти и диска. Когда активна тревога, издатели могут быть заблокированы. Это может выглядеть как замедление приложения со стороны производителя.
Выполните:
rabbitmq-diagnostics alarms
rabbitmqctl list_connections name user state channels send_pend recv_cnt send_cnt
Если активны тревоги памяти, выясните, занята ли память очередями, соединениями, неподтвержденными сообщениями, бинарными данными или плагинами. Если активны тревоги диска, освободите место или добавьте емкость, прежде чем пытаться протолкнуть больше сообщений через брокер.
Заблокированные соединения сами по себе не являются ошибкой. Это RabbitMQ говорит издателям замедлиться, потому что узел защищает доступность.
Размер сообщения может быть тихим виновником
Система, обрабатывающая 10 000 крошечных сообщений в секунду, может испытывать трудности с 500 большими сообщениями в секунду. Большие payloads увеличивают сетевой трафик, нагрузку на память, записи на диск, работу сборщика мусора и время обработки потребителя.
Если сообщения содержат большие документы, изображения, отчеты или большие массивы, рассмотрите возможность хранения payload в объектном хранилище или базе данных и отправки ссылки через RabbitMQ. Включите достаточно метаданных для маршрутизации и идемпотентности, но по возможности не используйте брокер для массового хранения.
Также проверьте выбор сжатия. Сжатие огромных payloads может уменьшить использование сети и диска, но увеличить загрузку ЦП. Поможет ли это, зависит от того, где находится узкое место.
Повторные попытки могут создать узкое место
Неисправная внешняя служба может превратить одно сообщение во множество попыток. Если потребители немедленно возвращают неудачные сообщения в очередь, они могут многократно обрабатывать одни и те же плохие сообщения, в то время как свежая работа ждет. Глубина очереди может расти, ЦП может быть занят, а полезной работы выполняется очень мало.
Ищите высокие показатели повторной доставки и повторяющиеся логи ошибок с одинаковыми ID сообщений. Если один и тот же payload постоянно терпит неудачу, переместите его из основного потока. Обмен dead-letter, очередь с задержкой повторных попыток или механизм запланированных повторных попыток дают зависимости время на восстановление и не позволяют ядовитым сообщениям блокировать нормальную работу.
Будьте осторожны со штормами повторных попыток. Если API недоступен в течение десяти минут, и каждое сообщение повторяется каждую секунду, восстановление становится сложнее, когда API возвращается. Используйте backoff. Ограничьте количество попыток. Сделайте окончательную неудачу видимой в очереди dead-letter с достаточным контекстом для расследования.
Идемпотентность также является частью диагностики производительности. Если потребитель повторяет попытку после частичного завершения работы, дубликаты могут создать конфликты в базе данных, ошибки уникального ключа или дополнительные внешние вызовы. Обработчик, который может безопасно обработать одно и то же сообщение дважды, гораздо легче масштабировать и восстанавливать.
Скорости в интерфейсе управления нуждаются в контексте
Интерфейс управления RabbitMQ полезен, но графики скоростей могут вводить в заблуждение, если читать одну линию. Высокая скорость доставки с низкой скоростью подтверждения означает, что работа раздается быстрее, чем завершается. Высокая скорость подтверждения с высоким количеством готовых сообщений может означать, что потребители работают, но не успевают догнать. Низкая скорость публикации во время инцидента может означать, что издатели заблокированы или ожидают подтверждений.
Смотрите на несколько скоростей вместе:
publish: сообщения, поступающие в обмены.deliver/get: сообщения, отправленные потребителям.ack: сообщения, завершенные потребителями.redeliver: сообщения, доставляемые снова после предыдущего сбоя или закрытия канала.
Для здоровой стабильной рабочей очереди скорости публикации и подтверждения должны быть близки с течением времени. Короткие всплески нормальны. Длинные разрывы означают, что отставание накапливается или уменьшается. Если количество повторных доставок резко возрастает, не просто добавляйте больше потребителей. Выясните, почему сообщения возвращаются.
Окна выборки имеют значение. График за одну минуту может скрыть пятисекундную задержку, которая вредит пользователям. График за одну секунду может сделать нормальную неравномерность похожей на хаос. Сопоставьте окно графика с задержкой, которая важна для ваших пользователей или внешних систем.
Отделите нормальное отставание от сломанного
Не каждое отставание является чрезвычайной ситуацией. Пакетная система может намеренно ставить работу в очередь в течение дня и обрабатывать ее ночью. Пользовательский рабочий процесс может быть нездоровым, если сообщения ждут тридцать секунд. Одна и та же глубина очереди может быть приемлемой в одной системе и критической в другой.
Определите сигнал на основе возраста, а не только количества. Количество сообщений говорит вам, сколько ждет; возраст сообщения говорит вам, отстает ли бизнес. Если ваш мониторинг может отслеживать возраст самого старого сообщения или сквозное время от публикации до подтверждения, он выявит замедления раньше, чем глубина очереди.
Привяжите оповещения к этому ожиданию. Оповещение о 10 000 сообщениях может быть шумным для ночной очереди экспорта и слишком поздним для очереди сброса пароля. Оповещение о «самом старом сообщении старше целевого показателя обслуживания» обычно ближе к тому, что волнует пользователей.
Одна горячая очередь остается одной горячей очередью
Добавление узлов кластера не автоматически разделяет одну очередь между всеми узлами. Одна горячая очередь может оставаться ограниченной своим лидером, своими потребителями и своим путем хранения.
Если одна очередь содержит несвязанные типы работы, разделите ее по реальному поведению обработки. Например, изменение размера изображений, отправка электронной почты и обработка платежей не должны делить одну общую очередь jobs, если у них разные требования к задержке и повторным попыткам. Отдельные очереди позволяют масштабировать потребителей независимо и изолировать ядовитые сообщения.
Если один тип работы все еще слишком горячий, шардируйте только тогда, когда требования к упорядочиванию позволяют это. Шардирование по ID клиента, арендатору, региону или другому стабильному ключу может работать, но добавляет сложность в маршрутизацию и эксплуатацию. Не шардируйте только для того, чтобы избежать исправления медленного обработчика.
Спокойный порядок диагностики
При инциденте я использую следующий порядок:
- Проверьте тревоги: память, диск и заблокированные соединения.
- Проверьте счетчики очереди: готовые, неподтвержденные, потребители.
- Проверьте логи потребителей и время обработчика.
- Проверьте prefetch и распределение неподтвержденных сообщений на каждого потребителя.
- Проверьте задержку подтверждений издателя и возвращенные сообщения.
- Проверьте задержку диска и нагрузку на ресурсы узла.
- Проверьте размер сообщения и недавние изменения payload.
- Только после этого меняйте топологию или добавляйте узлы брокера.
Этот порядок предотвращает распространенную ошибку: масштабирование брокера, когда узкое место в воркере, или масштабирование воркеров, когда узкое место на диске.
RabbitMQ обычно очень ясен, как только вы читаете правильные счетчики. Растущее количество готовых сообщений говорит о том, что работа ждет. Растущее количество неподтвержденных сообщений говорит о том, что работа выполняется, но не завершается. Заблокированный издатель говорит, что брокер защищает себя. Относитесь к каждому сигналу как к подсказке, и исправление станет гораздо менее драматичным.