Избор на MQ за проект с голямо натоварване
Съвременните мащабируеми системи се състоят от микроуслуги, всяка от които отговаря за своята ограничена задача. Тази архитектура ви позволява да предотвратите прекомерния растеж на изходния код и да контролирате техническия дълг.
В нашия проект има десетки микроуслуги, всяка от които е запазена: две или повече абсолютно идентични копия на услугата са инсталирани на различни физически сървъри и клиентът (друга микроуслуга) има достъп до всяка от тях независимо.
Ако дадена микроуслуга спре да реагира поради срив, нейните клиенти трябва незабавно да бъдат пренасочени към режим на готовност. За контролиране на потока от заявки често се използват така наречените опашки от съобщения.
Опашката, която наскоро използвахме, престана да ни устройва по отношение на устойчивостта на грешки и я заменихме. По-долу споделяме нашия опит при избора.
Доказано решение
Лидерът по популярност сред разработчиците е RabbitMQ. Това е изпитано във времето решение от корпоративен клас с гаранции за доставка, гъвкава система за маршрутизиране и поддръжка за всички видове стандарти. Мениджърите на проекти го харесват така, както купувачите на компютри обичаха IBM PC в началото на 80-те. Тази любов е най-точно изразена с фразата "Никой никога не е бил уволнен, защото е купил IBM."
RabbitMQ не ни устройваше, защото е бавен и скъп за поддръжка.
Производителността на RabbitMQ не надвишава десетки хиляди съобщения в секунда. Това е добър резултат за много приложения, но напълно незадоволителен за нашия случай.
Конфигурирането на RabbitMQ клъстер не е лесно, отнема ценни ресурси за devops. Освен това чухме от страната на ушите си за оплаквания относно работата на клъстера - той не знае как да обедини опашки с конфликти, възникнали в ситуацията на „разделения мозък“ (когатопоради прекъсване на мрежата се образуват два изолирани възела, всеки от които се счита за основен).
Опашка на базата на разпределен дневник
Разгледахме Apache Kafka, който се роди в LinkedIn като система за агрегиране на журнали. Kafka е в състояние да изтръгне повече производителност от дисковата подсистема от RabbitMQ, защото записва данни последователно (последователен I/O), а не произволно (произволен I/O). Но няма гаранция, че записът на диск винаги ще се извършва последователно.
В Kafka данните са разделени на секции (partition) и за да спази реда на доставка, всеки получател на съобщения чете данни от точно една секция. Това може да доведе до блокиране на опашката, когато получателят по някаква причина обработва съобщенията по-бавно от обикновено.
Освен това е необходима отделна услуга (zookeeper) за управление на Kafka cluster, което отново усложнява поддръжката и натоварва devops.
Не сме готови да рискуваме загуба на производителност в производството, затова продължихме търсенето си.
„Гарантирана“ доставка на съобщение
Има страхотен знак от Джеф Дийн, ветеран от Google (работи там от 1999 г.):
Можете да видите, че записът на диск е 15 пъти по-бавен от изпращането по мрежата.
Това е вярно, но няма гаранция. В крайна сметка, ако подателят се срине, докато съобщението се изпраща, или самият процес на опашката се срине, преди да бъде записан на диска, съобщението ще бъде загубено. Оказва се, че опашката само създава илюзията за гаранция за доставка и съобщенията все още могат да бъдат загубени.
Опашки с висока производителност
За да не губите съобщения в процеса на опашката, можете просто ... да премахнете процеса на опашката и да го замените с библиотека, която е вградена в процеса на микросервиз.
Много разработчици са запознати сZeroMQ библиотека. Той показва фантастична скорост, усвоявайки милиони съобщения в секунда. Той обаче (по идеологически причини) няма вградени инструменти за мониторинг и управление на клъстери, така че при използването му натоварването на devops е още по-голямо. Продължихме да търсим по-практични варианти.
СУБД опашка?
В един момент почти се отчаяхме и ни се стори, че ще бъде по-лесно да напишем сами опашката върху СУБД. Това може да бъде SQL база данни или едно от многото NoSQL решения.
Например, Redis има специални функции за внедряване на опашки. Тъй като Redis съхранява данни в паметта, производителността е страхотна. Тази опция беше разумна, но беше смущаващо, че добавката Sentinel, предназначена да групира няколко възела на Redis, изглеждаше някак изкуствено, сякаш прикрепена отстрани.
Когато използвате класическа СУБД, ще трябва да използвате техниката на „дълго запитване“, за да получавате съобщения. Това е грозно и е изпълнено със забавяне на доставката. И не исках да пиша на коляно.
Интуицията подсказа, че не сме първите, които търсят опашка с разумни изисквания за производителност и лекота на администриране и през 2017 г. нашата задача трябва да има готово решение.
Намерено решение: NATS
NATS е сравнително млад проект, създаден от Дерек Колисън, който има над 20 години опит в работата с разпределени опашки от съобщения.
По отношение на производителността NATS превъзхожда всички опашки с „гарантирана доставка“. NATS е написан на Go, но има клиентски библиотеки за всички популярни езици. В допълнение, клиентите на NATS също познават топологията на клъстера и могат да се свържат отново сами в случай на загуба на комуникация с техния възел.
Резултати от употреба в производството
Тъй като NATS не записва съобщения на диска, услугите за получаване трябва да излязат грациозно, като първо отменят абонамента за нови съобщения, след това обработват получените преди това съобщения и едва след това спират процеса.
Изпращащите услуги трябва да опитат отново да изпратят съобщението в случай на грешки. (Това обаче не е специфично за NATS и се изисква да се направи при работа с всяка опашка).
За да знаем със сигурност за изгубените съобщения, направихме най-простото регистриране: записваме часа на изпращане и часа на получаване.
Инсталирахме процеса NATS на всички виртуални машини с микроуслуги. Според резултатите от наблюдението в рамките на 2 месеца NATS не е загубил нито едно съобщение.
Доволни сме от избора си. Надяваме се, че нашият опит ще ви бъде полезен.
Hardcore conf в C++. Каним само професионалисти.