1. Немного воды, введение

    1.1 Краткое введение в RabbitMQ

    1.2 Почему безотказные очереди так важны?

  2. Трудности, возникшие при проектировании решения

  3. Стандартные механизмы RabbitMQ и почему они не подошли

  4. Описание работы найденного решения

  5. Заключение


Немного воды, введение

Краткое введение в RabbitMQ

RabbitMQ - это открытая реализация протокола AMQP (Advanced Message Queuing Protocol), является мощным и гибким брокером сообщений. Он обеспечивает надежное и эффективное взаимодействие между компонентами системы, предоставляя разработчикам инструменты для создания гибких и масштабируемых архитектур.

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

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

Почему безотказные очереди так важны?

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

Трудности, возникшие при проектировании решения

Представим себе сценарий, где несколько компонентов системы обмениваются важными сообщениями. Однако, из-за временной недоступности одной из систем, отправка сообщений может временно не удаваться. В таких ситуациях неизбежно возникает потребность в повторных попытках доставки. Безотказные очереди в RabbitMQ идеально решают эту задачу, обеспечивая надежное сохранение сообщений до тех пор, пока недоступная система не станет снова доступной. Это обеспечивает не только гарантированную доставку, но и защиту от потери данных в случае повторных сбоев, создавая устойчивый механизм взаимодействия между системами.

Стандартные механизмы RabbitMQ и почему они не подошли

В мире RabbitMQ решение проблемы гарантированной доставки сообщений находится в "quorum" очередях, предлагаемых "из коробки". Эти очереди предоставляют параметр x-delivery-count, позволяющий контролировать количество попыток отправки сообщения, а также параметр dead-letter-exchange, определяющий, куда направить сообщение после превышения лимита попыток.

Однако, существует заметная проблема – отсутствие встроенной задержки. При отклонении сообщения оно мгновенно возвращается в очередь и снова подвергается попытке обработки. Для решения этого вопроса можно обратить внимание на плагин rabbitmq-delayed-message-exchange. Однако, он предоставляет задержку только на уровне обменника, что позволяет активироваться только после перенаправления сообщения после заданного числа попыток. Это решение всё равно придает гибкость и дополнительный контроль над процессом обработки сообщений, но мы пошли своим путём.

Механизма работы выбранного решения

Сразу приведу схему работы, после чего постараюсь подробно описать что на ней происходит.

Схема работы механизма безотказных очередей
Схема работы механизма безотказных очередей

В представленном механизме обработки сообщений, путь сообщения - предположим, это "заказ", начинается с его направления в обменник с именем "order". Оттуда, оно попадает в соответствующую первичную очередь "order".

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

Если количество попыток не превышает установленный лимит, сообщение направляется в обменник "order_retry" и, впоследствии, в соответствующую очередь. В случае превышения лимита, механизм оповещения активируется, и сообщение направляется в очередь "order_error" через соответствующий обменник.

В очереди "order_retry" сообщение хранится определенное время, задаваемое параметром x-message-ttl (в данном случае 2 минуты), после чего оно автоматически возвращается в исходную очередь с использованием параметра x-dead-letter-exchange.

Такой путь сообщения повторяется установленное количество раз, после чего оно окончательно попадает в очередь "order_error". Здесь происходит логирование, в будущем планируется автоматизировать этот процесс.

В очереди "order_error" мы можем анализировать причину, по которой сообщение попало туда, и предпринять необходимые шаги для устранения ошибки. Например, если причина - временная недоступность системы, мы можем вручную перенаправить сообщение в основную очередь, когда система снова станет доступной. Этот гибкий механизм обеспечивает надежное управление обработкой сообщений и возможность быстрого реагирования на ситуации ошибок, при этом не теряя сами сообщения.

Заключение

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

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

Если всё же вы испытываете необходимость "пощупать" как это работает - вот пример реализации данного механизма на PHP и библиотеки php-amqplib.

Комментарии (6)


  1. unstopppable
    14.11.2023 05:03

    Так из коробки же, если коньюмер упал, сообщение вернется в ready и его подхватит другой, или оно останется ждать


    1. Foxion Автор
      14.11.2023 05:03

      Тут же вопрос не совсем в consumer, а в других системах, которые могут не отвечать n количество времени. Ну и в данном механизме так же нет задержки, т.е. сообщение будет моментально попадать в другой consumer


      1. zubrbonasus
        14.11.2023 05:03

        Если сообщение содержит неверные данные и не может быть по этой причине быть обработанным, тогда возврат в очередь может заблокировать весь поток. Лучше создавать новое сообщение которое попадёт в конец очереди.


  1. savostin
    14.11.2023 05:03
    +1

    С мобилки не смог найти, но помню что такой вопрос решался через ttl, оттуда во вторую очередь с большим ttl и оттуда обратно в первую. Не помню как с количеством раз решалось, наверное количеством очередей. Но в Вашем случае решение на уровне приложения, а не очереди. Те в каждом консьюмере надо решать вопрос отдельно.


    1. Foxion Автор
      14.11.2023 05:03

      Да, в моём случае очереди полностью управляются со стороны приложения. Соответственно есть "Базовый Consumer" от которого наследуются остальные.


      1. savostin
        14.11.2023 05:03

        Это пока у Вас относительный монолит и одна команда.