Comments 16
Годнота!
+ следить чтобы было достаточно сокетов, если они закончатся по любой причине, будут сложно отлавливаемые баги
+ если по какой либо причине закончилось место под хранилище, с большой долей вероятности оно будет испорчено
+ Не все написанное выше подходит к quorum очередям
+ В кластерной конфигурации надо очень сильно следить за состоянием кластера - ошибки в репликации или внезапные наплывы данных могут привести к рассинхронизации и, как следствие, не полученным данным
+ Как минимум в старых версиях реббит чувствителен к другой нагрузке на процессоры, при видимой средней загрузке и большом количестве context switch'ей может внезапно обрывать соединения
+ При планируемой нагрузке больше 10к сообщений в секунду стоит выбрать другую технологию
+ Подключение с oauth, проверка ландшафта и прочие улучшения могут существенно снизить производительность
+ При получении данных с concurrency > 1 порядок сообщений на клиенте не гарантирован
+ Стандартная .net библиотека не очень хорошо работает с многопоточной отправкой на большом количестве сообщений и ещё хуже с получением
Спасибо за дополнения.
В этой статье я постарался сфокусироваться на практиках, которые касаются работы клиентских приложений и того, что находится в области их влияния, поэтому аспекты надёжности, связанные с конфигурацией брокера, его администрированием и мониторингом, остались за скобками. Как мне кажется, они заслуживают отдельной статьи =)
Также я стремился к универсальности и не стал углубляться в специфику разных структур данных. В новых структурах, в том числе в кворумных очередях, некоторые аспекты действительно идут «из коробки» (durability, persistence), однако рекомендации остаются в силе — если не задать эти свойства явно, можно получить не только ошибки несовместимости, но и неожиданные эффекты.
Например, если в виртуальном хосте с default-queue-type: quorum
объявить очередь без явного указания типа, то с durable: true
получится кворумная очередь, а с durable: false
— классическая.
А неперсистентные сообщения, опубликованные в кворумную очередь, могут потеряться при перезапуске брокера после выпадения в DLX, если конечная очередь окажется классической — кролик сохраняет исходный признак персистентности при dead lettering'е.
А можно подробнее про пункт "При планируемой нагрузке больше 10к сообщений в секунду стоит выбрать другую технологию"?
Какие проблемы возникают и какую технологию лучше выбрать?
в трех словах - оно не успевает.
https://www.rabbitmq.com/docs/migrate-mcq-to-qq :
A quorum queue can sustain a 30000 message throughput (using 1kb messages), while offering high levels of data safety, and replicating data to all 3 nodes in a cluster. Classic mirrored queues only offer a third of that throughput and provide much lower levels of data safety
Тут, на мой взгляд указана, производительность для единственной очереди на кластере в идеальных условиях, поэтому стоит закладывать производительность с запасом на непредвиденные ситуации
В моём опыте была ситуация, когда росла внутренняя очередь сервиса отправляющего данные или, в случае цепочки очередей, первая очередь переполнялась
Поэтому для чего-то более производительного есть смысл использовать кафку, но за это придется заплатить большей сложностью как развертывания/поддержки, так и разработки
Хм, а почему kafka сложнее развертывать и под нее сложнее разрабатывать? Скорее уж проще, там гораздо понятнее и логика работы и гарантии, да и настроек поменьше.
Если вы знакомы и с тем и с тем, то вы скорее будете выбирать технологию под конкретную задачу.
Если не знакомы ни с тем, ни с тем, то, условно, кривая обучения для кафки будет sqrt(x), а для RMQ e^x.
Кроме того, на мой взгляд, для кафки нужно очень хорошо подумать сначала и потом делать, реббит в этом смысле прощает немного больше
Плюс ,в энтерпрайзе, особенно если компания посажена на MS и в силу безопасности нацелена на on-prem, есть проблемы с линуксом и людьми которые его поддерживают, в то время как RMQ в дефолтной конфигурации, даже без кластеризации, будет прекрасно работать под win.
если устанавливаете concurrency = 1, то и prefetch_count можно спокойно поставить в 1
или уже использовать kafka у которой очередность сообщений внутри партиции гарантирована "с рождения".
но вообще, про concurrency - это очень ценное замечание!
мало кто читает документацию внимательно, но зато потом удивляются "у меня же очередь! почему у меня сообщения обрабатываются не в том порядке, в котором я их записал?!"
Прошу дать совет: есть очередь из задач, примерно 120млн шт(например id, datetime) и надо их обрабатывать - сходить в другой сервис, получить данные и вернуть ответ в следующую очередь
Положить сразу 120млн сообщений в очередь некрасиво, я сделал доп очередь, в котором лежат сообщения в виде { minId, maxId}, далее обработчик извлекает все id в этом диапазоне и отравляет в следующую очередь
Что я сделал: при обработке minId, maxId проверяю длину следующий очереди, и если она больше чем X, то delay(t sec) , nack ()
В итоге у меня все очереди не превышают заданных лимитов, и я спокоен что ничего не утечет
Нормальный ли этот подход?
Или в корне все криво?
Ваше решение выглядит продуманным и вполне жизнеспособным.
На всякий случай напишу про тонкий момент, связанный с типами очередей. Если сейчас вы используете классические очереди, а со временем решите мигрировать на кворумные, то стоит иметь в виду, что кворумные очереди, в отличие от классических, по умолчанию делают requeue в конец очереди. Привычного поведения можно будет добиться, задав ограничение на количество доставок (delivery-limit).
Я бы предложил разработать решение без очередей, через какое-то другое апи.
Годно но мало. Хотелось бы ещё про HA-(и не только) полиси, кластер, vhost-ы и вот это всё. Ждём продолжения.
У очереди, куда приходят «умершие» сообщения, можно указать время их жизни (
message-ttl
), а уже упомянутые свойстваdead-letter-exchange
иdead-letter-routing-key
задать так, чтобы сообщения возвращались в исходную очередь. В результате сообщения будут автоматически отправляться на повторную обработку с заданной задержкой.
Это правда очень удобный способ обеспечить повторную обработку через таймаут после ошибки, но как принято в этой схеме обеспечивать порядок обработки сообщений?
То, что попало в DLX, ведь не блокирует обработку того, что продолжает поступать в изначальную очередь. А сквозной порядок через две очереди Rabbit не поддерживает, если не ошибаюсь.
Лучшие практики для надёжной работы с RabbitMQ