Кто виноват и что делать? :)
(repo1@repo1)1> mnesia:info().
---> Processes holding locks <---
---> Processes waiting for locks <---
---> Participant transactions <---
---> Coordinator transactions <---
---> Uncertain transactions <---
---> Active tables <---
rabbit_queue : with 10 records occupying 575 words of mem
rabbit_user_permission: with 1 records occupying 351 words
of mem rabbit_config : with 0 records occupying 316 words
of mem rabbit_reverse_route: with 10 records occupying 536
words of mem rabbit_route : with 10 records occupying 536
words of mem rabbit_exchange: with 7 records occupying 496
words of mem rabbit_durable_route: with 1 records occupying
357 words of mem rabbit_durable_queue: with 1 records
occupying 345 words of mem rabbit_durable_exchange: with 7
records occupying 496 words of mem rabbit_vhost : with 1
records occupying 330 words of mem rabbit_listener: with 2
records occupying 362 words of mem rabbit_user : with 1
records occupying 333 words of mem schema : with 13
records occupying 1891 words of mem ===> System info in version
"4.4.13", debug level = none <=== opt_disc. Directory
"/mnt/rabbitmq/mnesia/repo1@repo1" is used. use fallback at restart =
false running db nodes = [repo2@repo2,repo1@repo1]
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [rabbit_exchange,rabbit_listener,rabbit_queue,
rabbit_reverse_route,rabbit_route]
disc_copies = [rabbit_config,rabbit_durable_exchange,
rabbit_durable_queue,rabbit_durable_route,rabbit_user,
rabbit_user_permission,rabbit_vhost,schema]
disc_only_copies = []
[{repo1@repo1,disc_copies},{repo2@repo2,disc_copies}] =
[schema,rabbit_user, rabbit_vhost,
rabbit_durable_exchange,
rabbit_durable_queue,
rabbit_durable_route,
rabbit_config,
rabbit_user_permission]
[{repo1@repo1,ram_copies},{repo2@repo2,ram_copies}] = [rabbit_listener,
rabbit_exchange,
rabbit_route,
rabbit_reverse_route,
rabbit_queue]
155 transactions committed, 0 aborted, 2 restarted, 7 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
ok
(repo2@repo2)1> mnesia:info().
---> Processes holding locks <---
---> Processes waiting for locks <---
---> Participant transactions <---
---> Coordinator transactions <---
---> Uncertain transactions <---
---> Active tables <---
rabbit_queue : with 10 records occupying 607 words of mem
rabbit_user_permission: with 1 records occupying 351 words
of mem rabbit_config : with 0 records occupying 316 words
of mem rabbit_reverse_route: with 10 records occupying 536
words of mem rabbit_route : with 10 records occupying 536
words of mem rabbit_exchange: with 7 records occupying 496
words of mem rabbit_durable_route: with 1 records occupying
357 words of mem rabbit_durable_queue: with 1 records
occupying 341 words of mem rabbit_durable_exchange: with 7
records occupying 496 words of mem rabbit_vhost : with 1
records occupying 330 words of mem rabbit_listener: with 2
records occupying 362 words of mem rabbit_user : with 1
records occupying 333 words of mem schema : with 13
records occupying 1891 words of mem ===> System info in version
"4.4.13", debug level = none <=== opt_disc. Directory
"/mnt/rabbitmq/mnesia/repo2@repo2" is used. use fallback at restart =
false running db nodes = [repo1@repo1,repo2@repo2]
stopped db nodes = []
master node tables = []
remote = []
ram_copies = [rabbit_exchange,rabbit_listener,rabbit_queue,
rabbit_reverse_route,rabbit_route]
disc_copies = [rabbit_config,rabbit_durable_exchange,
rabbit_durable_queue,rabbit_durable_route,rabbit_user,
rabbit_user_permission,rabbit_vhost,schema]
disc_only_copies = []
[{repo1@repo1,disc_copies},{repo2@repo2,disc_copies}] =
[schema,rabbit_user, rabbit_vhost,
rabbit_durable_exchange,
rabbit_durable_queue,
rabbit_durable_route,
rabbit_config,
rabbit_user_permission]
[{repo1@repo1,ram_copies},{repo2@repo2,ram_copies}] = [rabbit_listener,
rabbit_exchange,
rabbit_route,
rabbit_reverse_route,
rabbit_queue]
76 transactions committed, 3 aborted, 0 restarted, 3 logged to disc
0 held locks, 0 in queue; 0 local transactions, 0 remote
0 transactions waits for other nodes: []
ok
В качестве развёрнутого ответа, опубликую краткую версию моего
отчёта, который когда-то писал по работе (сейчас он особой ценности
уже не имеет, так как раббит мы решили не использовать). Тут опущены
некоторых интимные подробности и исследований реализации (читай
копания в исходниках :))
Введение.
Разработчиками предоставляется возможность кластеризации серверов
rabbitMQ.
Основное назначение предоставляемого кластера,- распределение
входящей нагрузки и базовые механизмы восстановления системы в случае
отказа.
Инструкцию по запуску серверов и включению их в кластер можно
посмотреть на: http://www.rabbitmq.com/clustering.html
Основная задача данного отчета, - дать представление о возможностях
использования и базовом внутреннем устройстве предоставляемого
решения.
Устройство кластера RabbitMQ.
Кластерное решение rabbitMQ преимущественно основано на механизмах
репликации распределенной БД mnesia.
Информация о очередях (queues), обменниках (exchanges), и маршрутах
(bindings) хранится в таблицах БД,
при этом, таблицы серверов объединенных в кластер поддерживаются в
непротиворечивом состоянии средствами mnesia (реплицируются).
Для подключения узла к кластеру, необходимо, чтобы приложение-сервер
было на нём остановлено, и таблицы БД сервера имели непротиворечивое
состояние с таблицами кластера (зачастую, практически, этого можно
добиться исключительно сбросом таблиц сервера).
Связь и распределение нагрузки между серверами кластера возможна за
счет механизма маршрутизации, организованного следующим образом:
Репликация обменников, маршрутов и очередей позволяет клиенту
логических равнозначно обратится к любому из серверов кластера
(произвести публикацию сообщения, подписаться на очередь...).
Физически маршрутизация публикуемых сообщений осуществляться на
сервере к которому произошло обращение, и в случае если очередь в
которую необходимо положить сообщение физически находится на другом
сервере, оно будет в неё оттранслировано (в базе хранится PID процесса-
очереди).
Механизмы отказоустойчивости.
durable объекты.
Сохранение на диск информации об объектах (очередях, обменниках,
маршрутах), с целью их восстановления при перезапуске сервера. Для
того, чтобы воспользоваться этим механизмом необходимо объявлять
объекты с флагом durable = true.
В случае кластера такой механизм может быть полезен, исключительно для
обеспечения возможности перезапуска кластера с нуля (после остановки
всех узлов), это обусловлено тем, что за время которое узел был
неактивен, состояние таблиц кластера, могло изменится => для его
подключения к кластеру потребуется сброс таблиц базы.
persistent сообщения.
Сообщения такого типа дублируются в файловой системе, до момента их
полной обработки. В случае перезапуска сервера, они будут
восстановлены из слепка в фс и доставлены адресатам. Необходимо
сделать два важных замечания:
* Сообщения сохраняются локально в фс (не в таблицы mnesia !) и
следовательно не реплицируются другими узлами кластера.
* Сообщения именно дублируются в фс (т.е их экземпляр есть как в
фс так и в памяти), это может быть важно, при попытки таким образом
"пропихнуть" сообщения большого объёма (для записи persistent
исключительно в фс сушествует плагин !!!ссылка!!!)
транзакции.
Необходимо на практике проверить реализацию механизма. (как выяснилось
позже, и тут тоже шлак).
Недостатки.
С точки зрения распределенных систем описанную реализацию назвать
кластером можно с натяжкой, это обусловлено следующими моментами:
1. Для процессов пользователей система не выглядит единым целым,
подключение осуществляется к конкретному её узлу (хост, порт...).
2. Для клиентов не обесчена прозрачность отказов (это отчасти
обусловлено пунктом 1, отчасти особенностями применённого подхода
репликации). В случае отказа узла, соединение разрывается явным
образом, и для продолжения работы (даже если оно возможно), клиент
должен содержать логику переподключения к другому узлу системы.
К другим недостаткам можно отнести, отсутствие централизованного
инструмента/механизма контроля состояния (желательно в духе OTP) и
управления кластером.
Как можно заметить, большая часть недостатков лежит в области
обеспечения отказоустойчивости брокера. Сами разработчики утверждают,
что задача повышения надежности имеется у них в roadmap, однако не
имеет высокого приоритета, так как по их словам: "не так много людей
которым это действительно необходимо".
Возможные решения проблемы отказоустойчивости.
Подытоживая всё вышесказанное, можно выделить следующие возможные
варианты решения проблем обеспечения отказоустойчивости с
использованием продукта rabbitMQ:
Создание библиотеки связи.
Идея решения заключается в том, чтобы создать обертку библиотеки-
транспорта, способную самостоятельно переключаться на другие узлы
кластера, в случае отказа текушего узла.
С учетом, особенностей имеющегося кластерного решения, потребуется,
пересоздание очередей на новом экземпляре кластера.[ проверить
реализованно ли это автоматически пока что руки не дошли ]
Так же необходим механизм обеспечения надежности передачи сообщений
(предлагаются два варианта):
Использование встроенного механизма транзакций.
При отправке всех "важных" сообщений используется механизм транзакций.
В случае разрыва соединения, и по истечению таймаута все транзакции
признаются "неудачными".
Данный способ видится наиболее простым в реализации, но влечет за
собой увелечение задержек при передачи сообшений.
Общее хранилише persistent соосбщений.
Суть решения в отправке всех "важных" сообщений как persistent и
создании их обшего разделяемого хранилища.
Без модификации исходных кодов rabbitMQ в качестве такого хранилища
можно использовать общий сетевой ресурс (в том числе DFS).
С модификацией исходного кода любую подходящую СУБД.
Потребуется модификация исходного кода сервера позволяющая всем узлам
кластера работать с единым persistent хранилишем.
Но она есть в mnesia, насколько я понимаю, и именно mnesia я пытался
заставить реплицировать, что что-то явно делаю не так.
> Реплицирует он конфигурацию броккера (т.е например эксчендж
> объявленный на одной ноде кластера виден и на другой),
> что позволяет масштабировать производительность брокера.
> Однако надёжности без дополнительных плясок добится не получится.
> + эрланговсий клиент к рабиту сам не умеет переключаться между нодами
> кластера (по крайней мере это справедливо для 1.6, нужен ручной
> допил).
Клиент у меня на руби и его я уже научил переключатся между нодами,
если натыкается на неработающую (или текущая вырубилась).
> В качестве развёрнутого ответа, опубликую краткую версию моего
> отчёта, который когда-то писал по работе (сейчас он особой ценности
> уже не имеет, так как раббит мы решили не использовать).
А какая альтернатива? Что вы взяли на вооружение вместо кролика?
Там всё не так просто, чтобы мнезия начала реплицировать сообщения, их
надо туда записывать, чего не делается.
Если будете слать сообщения с опцией persistent, то они начнут
сторится на диск (если мне память не изменяет, делается это опять таки
не через мнезию, а через собственный персистер). Но это поможет лишь
тем, что после перзапуска упавшей ноды кластера, она эти сообщения
подберёт, другая живая нода кластера не умеет подбирать сообщения
упавшей ноды
Используем qpid.
Но вообще, классов задач в которых требуется репликация обеспечиваемая
брокером не так и много (т.е таких где профиты перевешивают накладные
расходы).
В случае если у вас обмен сообщений идёт по схеме отличной от "много
клиентов обращаются к множеству равнозначных серверов-обработчиков
разделяющих общее состояние" то возможно, лучше подойдёт вариант с
обычными ack, перепосылками и.т.п.
Выбор как всегда идёт между производительностью и надёжностью.
On Sun, 11 Jul 2010 03:07:21 -0700 (PDT)
Igor Karymov <ingh...@gmail.com> wrote:
> Там всё не так просто, чтобы мнезия начала реплицировать сообщения, их
> надо туда записывать, чего не делается.
OMG, очевидно в этом моя главная ошибка. Я отталкивался от мысли что
сообщения лежат в mnesia (видел таблицы rabbitmq_queues,
rabbitmq_durable_queues) и можно выстраивать собственные конфигурации
репликации не вмешиваясь в работу кролика.
Для меня критична надёжность. Огромного количества сообщений у меня
нет, даже пары сотен в секунду.
Чёрт! Какая же досадная ошибка :(
При такой нагрузке я бы посмотрел на постгрес и его очереди. Кролик
прекрасно себя зарекомендовал на тысячах сообщений в секунду, но я бы
не сказал, что надежность данных — это его основная фича.
Вы уже проверяли? Если вам нужна надежность, вам в любом случае нужно много IO.
Нет, конечно же я не проверял. Я надеялся что кролик удовлетворит мои
потребности.
Короче, будем думать как быть дальше. Может и кролика захачим :)
полазил по исходникам
src/rabbit_amqqueue.erl
store_queue(Q = #amqqueue{durable = true}) ->
ok = mnesia:write(rabbit_durable_queue, Q, write),
ok = mnesia:write(rabbit_queue, Q, write),
ok;
store_queue(Q = #amqqueue{durable = false}) ->
ok = mnesia:write(rabbit_queue, Q, write),
ok.
версия tip
Записали конфигурацию очереди в мнезию. В случае если она дурайбл в
одну таблицу (видимо пишушиюся на диск), в противном случае в другую.
Репликация сообщений от этого не появилась.
Если уж хочется, всё проверить самому, то вот самый простой способ:
на ноде где находится рабит забустить tv:start(). и поглядеть
мнезийные таблицы.