Увидев вводный пост про AMQP решил поделиться своей практикой использования этого механизма.

Ещё задолго до начала внедрения AMQP у нас был полный зоопарк из различных обработчиков фоновых задач: delayed_job , starling + workling и т.п. Плюс к этому, ещё активно использовался HTTP ping между серверами и juggernaut для пуш-канала на браузер. Ах, ещё забыл брутальное оповещение по IP multicast.

Весь этот перечень страшных слов работает, но уж очень он разнообразен и обширен, решили урезать. Чего же общего между HTTP ping-ом и старлингом? Да то, что это всё сообщения от источника к подписчикам. AMPQ был выбран как общий транспорт для таких сообщений.

Структура AMQP

AMQP — транспорт по доставке сообщений от источника (producer) к потребителю (consumer) через брокер. В брокере есть точки входа, к которым цепляются источники (точки обмена, exchanges) и точки выхода, очереди (queues), к которым цепляются потребители. Точки входа соединяются с очередями с помощью bindings — соединений. Об организации внутри брокера ниже поподробнее.

Особенности протокола

Важно отметить, что протокол AMQP по своей организации асинхронный и рассчитан на организацию consumer-а (потребителя) в event based стиле. Т.е. если у вас многонитевой потребитель, который обрабатывает задачи, у вас будет куча проблем: AMQP на это плохо рассчитан.

У каждого AMQP сообщения есть канал по которому оно передается (для мультиплексирования внутри одного TCP соединения). Ответы на запросы могут прийти когда угодно. Потребитель стоит писать с использованием EventMachine, libev или чего-то похожего.

Это очень важно, потому как иначе, если вы напишете:


declare_queue(..)
bind_queue(..)

то вы в этом месте будете получать под нагрузкой разрыв соединения с AMQP брокером. Дело в том, что второй запрос может уйти до выполнения на брокере первого и может случиться что попытка подсоединить очередь будет выполнена до её создания. На такое брокер однозначно рвет соединение.

Брокер

Брокер сразу был выбран RabbitMQ по той причине, что он конфигов не имел, а ApacheMQ имел энтерпрайзного вида многокилобайтное XML-ное полотно, в котором ни один пункт нам не понадобился. Нам, как знатным макоёводам первый выбор понравился гораздо больше и пока не жалели.

Надо сразу отметить, что ApacheMQ выглядит более обширной реализацией, но в нём нет поддержки протокола AMQP (да и вообще у них на сайте создается впечатление, что они к AMQP никакого отношения не имеют). Зато у RabbitMQ есть экспериментальная поддержка протокола Stomp, который есть и у ApacheMQ. Впрочем, мы остановились на хождении по AMQP, потому что для рельсов есть прекрасная реализация написанная Аманом Гуптой. Не, честно, индус, а хорошо написал. Почти не пришлось допиливать.

Источник

Такого же удобного способа складывать задачи в AMQP, как в Delayed Job (UserMailer.send_later(:deliver_register_confirmation, user)) мы ещё не нашли, но просто руки ещё не дошли. В источнике сообщений вполне можно использовать синхронную реализацию протокола AMQP, потому что задача источника плюнуть сообщением и забыть про него. Учитывая, что это зачастую веб-сервер, ждать точно ничего не надо.

Организация внутри брокера

AMQP — не просто FIFO канал. Точки входа (exchanges) могут быть разных типов и каждый тип обуславливает правила доставки сообщений по присоединенным очередям.
Так же вы можете играть настройками персистентности (как это слово перевести?) очередей, точек обмена и сообщений.

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

Очередь фоновых задач

  1. Создается durable (персистентная) точка обмена fanout с названием background;
  2. Создается durable очередь с названием background и соединяется с точкой обмена соединением с произвольным ключом;
  3. Сообщения шлются в эту точку обмена с флагом persistent;
  4. Фоновые демоны подсоединяются к этой очереди с флагом ACK, что говорит о том, что пока этот демон не отмаркирует успешное приём сообщения, оно из очереди не уйдет;

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

Чатик

Классический пример для передачи сообщений — работа онлайн-чата (Juggernaut). Для упрощения, положим что Flash-клиенты цепляются сразу к AMQP брокеру (хотя на самом деле вы не должны хотеть этого).

  1. Создается durable точка обмена direct с названием users;
  2. Под каждого клиента создается auto_delete очередь. Я не рекомендую использовать такую возможность AMQP, как нулевые названия очередей, когда сервер сам создает — вы не сможете по ним отлаживаться. Лучше называть как-то по своему, с ID пользователя текущей временной меткой до миллисекунды. Во-первых будете знать, когда очередь создана, во вторых кем. При отладке поможет;
  3. Очереди подсоединяются к точке обмена с маршрутизацией (binding key) совпадающей с userID;
  4. Для посылки сообщения пользователю веб-сервер соединяется с точкой обмена users и шлет сообщение с routing key равным userID;
  5. Для реализации группового чата надо сделать пп. 1, 3 для точки обмена chats;
  6. Для широковещательной рассылки вида «сейчас будет отключение сервера» заводите точку обмена fanout all_users.

Заключение

Я привел лишь пару практических примеров использования AMQP. Сам протокол подразумевает гораздо более широкое использование (вплоть до передачи файлов), однако нам оно просто ещё не было нужно.

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

Сравнение брокеров

RabbitMQ основным протоколом предлагает AMQP и в качестве экспериментальных реализаций предлагает Stomp, email, REST, XMPP (джаббер).

ApacheMQ предлагает OpenWire и Stomp. AMQP я там не нашел, а самый хороший рельсовый клиент для ActiveMQ был именно для AMQP. Плюс C-шную библиотеку от RabbitMQ оказалось проще всего перевести на libev. Это подтолкнуло к выбору RabbitMQ.

Сравнение протокола AMQP-0.8 и 1.0

Начну с конца: я бы закладывался на 0.8. Реализации есть, работают, хлеба не просят. 1.0 в данный момент в состоянии драфта. Честно говоря глубоко и детально в спецификацию 1.0 я не вчитывался — лишь проглядел её. У меня, учитывая то, что и 0.8 мне за глаза, возникло ощущение: понахреновертили.

Наверное, AMQP становится безумно крут, когда сам начинаешь писать свои эксченджи для кастомных задач и гонять весь трафик через него. Но я лучше пошлю файл через nginx, чем через ActiveMQ брокер. У авторов стандарта наверное другие задачи и условия.

Резюме такое: я бы не стал связываться с драфтовым 1.0, который ещё не реализован по причине отсутствия самого стандарта.

crosspost: на хабрахабре

Sidebar