Комментарии 131
eBay перешел на микросервисную архитектуру, чтобы повысить масштабируемость и сократить время вывода новых функций на рынок.
Судя по внешнему виду, eBay до сих пор работает на Perl CGI и Apache. Я думал они уже лет 10 не "выводят новые функции на рынок"...
Как будто это что-то плохое
Судя по внешнему виду, eBay до сих пор работает на Perl CGI и Apache
А как вы определили что у ебей perl?
По данным https://www.wappalyzer.com/lookup/ebay.com/ у ebay самый продвинутый технологический стек: Marko.js, React, Node.js.
А внешний вид из девяностых. И удобство пользования.
Я с Вами полностью согласен.
Но изменив этот внешний вид, они рискуют получить волну хейта за тем, для кого новый вид неудобен, этим они сломают все виды роботов/парсеров/скриптов/снайперов на аукционах и так далее. Непонятно заранее при том, выиграют ли в деньгах.
Так и приходится жить в неопределенности, видимо. Было бы известно заранее, что новый облик точно пошёл бы в плюс, — сделали бы.
Вот к примеру хабр. Ну поменяли тут всё "на современный лад", и? Стало точно не лучше. То, что вся страница сразу не прогружается (комментарии грузятся теперь отдельно) и что страница даже будучи прогруженной постоянно лезет в интернет - уже год как это всё выкатили, а до сих пор плююсь, не могу привыкнуть. Раньше как, открыл в новых фоновых вкладках с десяток интересных статей не глядя - и можно хоть в метро, хоть в самолёт. А теперь? Пока за ушком не почешешь - страница не прогрузится, ни иллюстраций ни комментариев. А комментарии в 2/3 случаев полезнее статьи. Если бы на хабре не было комментариев - скорее всего я бы его вообще не читал бы..
Так что иБэй молодцы, понимают что важно, а что шелуха. Наверно. Мнение личное, прошу не пинать.
а то, какая все же связь между «внешний вид из девяностых» и «работает на perl cgi», полагаю, останется загадкой
универсального подхода не существует
Как бы, да.
Выбор инструмента зависит от целей, потребностей, возможностей.
Чистый монолит это, наверное, зло злющее, но это работает. По крайней мере, он мало зависит от наличия и квалификации девопсов: достаточно сервера приложений вроде wildfly. Зато мало проблем с распределенными транзакциями, оркестрацией и коммуникациями между модулями.
Чистые микросервисы - это нечто вроде всем известного сферического коня. В условиях "давай-давай" и "выпускаем в продакшен, а после отрефакторим и допилим" они в подавляющем большинстве случаев досточно быстро превращаются в редкостную помойку из костылей. Плюс очень высокая зависимость от квалификации девопсов. Только в книжках все гладко и кучеряво. На деле, увы - там не завелось, тут отвалилось, здесь легло, токены протухли. Особенно при развертывании в коммунальных средах, когда ресурсы централизованы и выделяются по заявкам.
Может быть, мне просто не везло :)))
быстро превращаются в редкостную помойку из костылей.
Это же в большей степени свойственно монолиту, разве нет? В случае микросервисного подхода при разработке нового можно учесть ошибки предыдущих и внести существенные изменения в архитектуру. Также можно постепенно, по мере необходимости, и относительно просто переносить функциональность со старого сервиса на новый. Да, с оркестрацией могут возникнуть трудности, но это всё-таки проще, как по мне, чем копаться в легаси, особенно когда оно без документации и написано без типизации.
Ну, рефакторинг монолита обычно проще (на нормальных языках). В микросервисах любые изменения за пределами одного микросервиса становятся крайне сложными.
Так что микросервисы протухают, обычно, много быстрее. Обычно - еще до выхода в продакшен (
С микросервисами основная проблема заключается в плохой коммуникации между разработчиками. Например, когда за сервис отвечает разработчик, которому все равно или который не хочет идти на компромисс, а иногда такой разработчик вообще находиться в другом отделе и нормальная коммуникация с ним невозможна. При грамотной организации работы хорошо реализуется принцип разделения интерфейсов и вносить изменения можно относительно бесполезно.
Ну бекенд без типизации это вообще что-то странное в любом случае :)
Вот оно, ключевое "внести существенные изменения в архитектуру". Когда код миновал стадию MVP и получил одобрение, то какая причина изменять архитектуру? Ведь работает же вроде, чего там менять. Заказчик именно из этого и исходит. А настоящие мины вылазят, как правило, потом. Прилетают новые требования. На полпути меняется база данных. Вместо "кролика" решают перейти на kafka... Уходят ключевые разработчики. Переделка архитектуры это выброшенное время, труд, деньги и настроение. Нет уж, допиливайте что есть, а там - будем поглядеть.
Не подумайте, я не топлю за монолит в противовес микросервисам (и наоборот). Если есть время, хороший архитектор (подчеркиваю - хороший, т.е. не брехло, выучившее модные словечки вроде DDD, gRPS, Reactive, Streams и т.п., а реально щупавший это), то и монолит, и микросервис можно сделать конфеткой. Конечно, я помню и о том, что нужен качественный системный анализ: для разработчиков это - даже в первую очередь.
Тема такая, что спорить тут можно до хрипоты и никто никого не переубедит, т.к. все зависит от множества факторов. Но можно и монолит сделать изящно. А можно и нет. Аналогично с микросервисамм
Самое частая проблема это когда производительность в одном месте упёрлась и дальше вертикально скейлить уже дорого. Да, можно горизонтально скейлить весь монолит, но иногда монолит слишком большой, а иногда это не просто именно из за архитектуры и нужно все равно допивать, чтобы монолит нормально горизонтально скейлился. Тогда уже проще выделить в отдельный сервис только то куда упёрлись и то что нужно скейлить
Нет проблем с вертикальным скейлом. Сегодня можно арендовать сервер в котором все ваши «большие данные» в ОЗУ влезут. Если вы не Гугл, или Фейсбук то у вас нет проблем с большими данными и вертикальным масштабированием.
Монолиты умеют скейлиться горизонтально, особенно, если изначально так проектировать.
Не только проектировать, но и реально использовать. Иначе всё равно будут баги, когда спроектировали, но на местах налажали.
Не всегда. Запихни в монолит ML часть (а такое бывает сплошь и рядом) и потом начинается: нам нужно куча CPU, и памяти, но только час в сутки и приходится платить за дорогой инстанс который используется по полной лишь малое количество времени. Не все могут/хотят выкидывать деньги на ветер. Проще выпилить часть в сервис и скейлить уже его, задеплоив оставшуюся часть на дешёвый инстанс
Я с вами согласен. Просто я изхожу из позиции начинающего разработчика, который, в основном, дорабатывает существующие решения. В случае микросервисов при применении грамотной архитектуры, новичку проще разделить приложение на составляющие и, соответственно, проще влиться в проект.
Микросервисы хороши когда ты большой и сервер уже не влезает в терабайт оперативки, а работать надо. И/или когда ты ещё и международная корпорация и надо по всей планете сервера, причем не везде нужны полные функции. И всё прекрасно.
А потом приходят любители карго-культа и говорят мол смотрите, гугол на микросервисах и успешен. Мы же хотим быть успешными как гугол. Будем делать микросервисы.
А потом тебя на собесе не берут на работу потому что ты стар и как это монолит тут лучше, все же используют микросервисы.
Но рыночек порешал - когда в деньгах становится понятно что микросервисы стоит строить на первые сто миллионов прибыли и никак не раньше - сразу становится понятно как чего. Ну и хайп сходит потихоньку.
Конечно есть исключения где лучше брать микросервисы на старте. Ещё кто-то может сказать что отдельный микросервис может разрабатывать отдельная команда, удобно. Только вот… это называется сервис, не микро, просто сервис.
Приятно слышать что спадает хайп. Я в нем поболтался какое-то время, оценил плюсы и минусы. Из неожиданных плюсов назову тот что для однопоточных языков может быть неплохой кейс с нарезкой по функциональным микросервисам и тогда некоторых микросервисов мы создаем больше инстансов, а других, менее нагруженных - меньше. Особенно хорошо когда это в монорепозитории лежит и по факту это многопоточность на уровне серверов, вместо приложения.
А вот из минусов, о которых почти все забывают - огромные расходы коммуникаций. Это же надо данные подготовить, сериализовать в протокол, отправить по сети, иногда на другую машину у другой стойке, там обработать пакет, десериализовать, обработать данные и потом также назад с ответом. Огромное количество мусорных вычислений. Кто об этом задумывается? Мало кто. А деньги то горят там по чуть-чуть. Про отладку запроса что идет через тройку микросервисов насквозь - вообще молчу.
С другой стороны когда ты гугл - это всё очень нужно, иначе ты не сможешь держать столько функций вместе. Это неизбежно, это эволюция роста. И тогда микросервисы делают своё дело. Так что я рад что прозревает сообщество потихоньку.
А вот из минусов, о которых почти все забывают - огромные расходы коммуникаций
Сюда же огромное увеличение сложности управления надежностью.
Большой сервис зависит от нескольких БД и какой-нибудь Кафки. Пока они работают, он тоже должен работать. Просто и понятно. Делать надежные БД люди уже десятилетия умеют.
А вот микросеррвис зависящий от пяти других микросервисов (которые в свою очередь от чего-то зависят) это проблема. Как определить его надежность? Что должно ломаться если сломалась зависимость? Как сделать эту систему не менее надежной чем систему зависящую только от БД?
И сюда же сложность эксплуатации. Я в живую видел людей годящихся несколькими своими микросервисами которые держат десятки тысяч РПС. Они же рассказывали что Джава не ок, мол у нас нагрузки. При этом у них входной пользовательский РПС дай бог пара сотен. Остальное они сами внутри накрутили. На предложение найти эти десятки тысяч РПС и снизить хотя бы до тысяч (это точно возможно) меня посылали очень далеко. Мол у них продуманная и очень правильная архитектура системы.
В итоге я пришел к выводу что самые надежные и понятные системы это системы с зависимостями только через БД. Кафку тоже БД считаем. Все взаимодействие по АПИ со внешними системами надо выносить куда-то в асинхронный код.
Пришел запрос от юзера для обработки которого нужно в три внешних сервиса сходить? Положите его в Кафку и другим сервисом прочитайте и сходите куда надо. Если даже внешние системы упадут вы не потеряете запрос юзера и покажете ему что все сохранено, ожидайте. Это приятнее чем ловить пятисотки.
Положите его в Кафку и другим сервисом прочитайте и сходите куда надо. Если даже внешние системы упадут вы не потеряете запрос юзера и покажете ему что все сохранено, ожидайте
Именно так. И не только запросы юзера, но и внутренние взаимодействия между сервисами
И да и нет.
Асинхронное взаимодействие с состоянием более хрупкое (проще сломать) и сложнее в администрировании. Синхронное в современных условиях, при наличии оркестрации в стиле Temporal - самый простой способ работы в современных распределенных системах.
Темпорал классный. Он как раз про асинхронность в том смысле как я это слово использовал. Вы сохранили запрос юзера и потом его обработали.
Синхронно это вы прямо на запросе юзера идете в кучку внешних сервисов и собираете ответ и возвращаете его юзеру / сохраняете в БД.
Так темпорал позволяет делать и так и так.
Т.е. пришел запрос, его сохранили, обрабатываем и отдаем ответ синхронно. Но если в процессе обработки проблемы - кидаем timeout или просьбу повторить запрос. В таком подходе гораздо меньше проблем с отсутствием response, нет проблем с "локальностью контекста" и гораздо ниже latency.
При этом во многих случаях при проблемах с обработкой вообще не надо ничего делать, просто вернуть ошибку, а темпорал нужен только на "самом верху", только при инициализации бизнес-процесса и только на случай крайне редких сбоев (благо современная инфраструктура позволяет сделать синхронный вызов не менее надежным, нежели кафка).
А почему в Кафку? Асинхронное взаимодействие хорошо только там, где без него действительно никак. По-умолчанию, всегда надо использовать RPC. Асинхронщина убивает перформанс и сильно запутывает разработку, отладку и траблшутинг.
Взаимодействие через БД создаёт проблему, что очень сложно модифицировать БД которую используют несколько команд. Для этого собственно и придумали микросервисы - одна база одна команда, за неё отвечающая
Так описано же, почему кафка. Потому что сходить надо в другие системы, а они в микросервисной архитектуре всегда могут упасть. И ваш сервис тоже может.
Поэтому нам нужна очередь с гарантиями доставки, например кафка в её роли.
Кому-то нужна, а кому-то не нужна. Нет никакого абсолютного решения.
Если это сервис обрабатывающий платежи - очевидно нужна. А если это сервис рекомендаций - то можно просто на фронтенде не отрисовывать этот блок (я на месте пользователя только бы обрадовался этому), нет ничего страшного что 0.01% запросов закончатся этим. С другой стороны это не бесплатно писать в стейтефул сервисы ни по железу, ни по стоимости сопровождения.
Проблема не в 0.01 проценте запросов. А в том что ваш сервис может прилечь из-за соседнего сервиса. Без которого можно было обойтись и обработать данные в нем потом. Прилечь ваш сервис может как на пару минут, так и на пару часов. Вы на это не влияете.
Ютуб без рекомендаций пару часов это потеря кучи денег. Прямо большой кучи.
Железа там ерунда как показывает практика. Поддерживать даже удобнее. У вас есть все промежуточные данные. На них можно смотреть и даже править при необходимости. Можно сделать поверх них очень хорошие логи.
А вот писать дольше и сложнее. Тут согласен. Но это плата надежность.
Вы к чему это? Нужна вам очередь или нет - это вопрос того, что вы делаете.
Если вы можете (бизнесово, логически) отдавать данные потребителю независимо от других сервисов - можете использовать вполне синхронное апи без очередей. Если запросы не на изменение данных, то даже с зависимостью от других сервисов можно делать запросы синхронно, да.
Если же речь идёт про запросы к другим сервисам на изменение данных - то обычно такие запросы нельзя терять\игнорировать, поэтому в свой сервис сохраняете в БД, остальных оповещаете по очереди (кафке в теме треда).
ПС: а ещё вопрос нагрузки важен, т.к. параллелить очередь очень легко - просто добавляйте обработчики (утрирую, в той же кафке это не всегда легко и понятно). Для синхронного апи вам потребуется дополнительный балансировщик входящих запросов.
другие системы, а они в микросервисной архитектуре всегда могут упасть. И ваш сервис тоже может
Да и кафка может.
Надежность Кафки обеспечить проще чем надежность нескольких произвольных сервисов.
Как минимум потому что Кафка не содержит бизнес логики, в нее не пишут фичи и вообще она обновляется раз в год ибо чаще не надо. Абстракции это хорошо.
Как уже написал @BugM - кафка это одна система, стабильная, за дней проще ухаживать.
В дополнение - есть паттерн https://microservices.io/patterns/data/transactional-outbox.html который подразумевает что если сервис работает, то работает (мы надеемся) и его БД, что сохранит наши данные даже при упавшей кафке, а после её поднятия система продолжит работать как надо.
А если у сервиса не работает БД, то и в кафку ничего слать не надо, мы не можем фиксировать изменения.
ПС: идеальных решений нет, но часть вопросов так можно закрыть.
Transactional outbox готовить надо уметь, иначе мы просто убьем БД, а за ней и всё остальное ляжет.
И я-таки видел, как TO убивает приличный кластер баз данных в некой компании с 200+ млн пользователей.
Вот тут я сам без опыта реального, знаю только что применяют многие (и видел работающие системы).
В небольшой БД конкретного микросервиса объемы и нагрузка должны быть небольшими, на самом деле. Вот если это аутбокс монолита - то можно получить пиковую нагрузку именно с аутбокса, хз как такое правильно решать, разносить в разные таблички возможно.
Доставки куда? Если сервис упал, то что толку от этих сообщений в кафке? Вы потом будете чесать репу и думать как обработать сообщения вручную? А проигнорировать ошибку и при синхронном взаимодействии никто не мешает.
Я заметил что в некоторых организациях упавший сервис, проблемы с перфомансом или не работающее приложение считается в порядке вещей. Но это конечно не так.
Условно, сервис А шлёт сообщение через кафку в сервис Б.
Сервис Б недоступен по любым причинам (нас, как разработчиков сервиса А это не волнует, может там питание выключили, может БД отвалилась, может сертификаты протухли). Мы как разработчики сервиса А положили сообщение в кафку и выполнили свою ответственность - сообщили о каком то событии.
Разработчики сервиса Б сервис подняли, сервис продолжил читать кафку с места, на котором он остановился. Дошел до нашего сообщения и его автоматически обработал.
Вручную обработка не требуется. Игнорировать "ошибки" никто не будет, т.к. их и нет во взаимодействии сервисов. А вот при синхронном взаимодействии этот сценарий не работает, что в том числе и решается очередями (кафкой к пример).
Сервис рано или поздно поднимется и обработает все накопившееся.
Сервисы падают. Это реальность. Обеспечивать 6 девяток доступности сложно и дорого. Зависимость от пяти сервисов с 5 девятками каждый дает вам полчаса недоступности в год по внешним причинам. Это обычно уже слишком плохо и такие зависимости использовать нельзя.
Тормоза это странная вещь. Можно сделать и тормозной сервис и быстрый на любой архитектуре.
@MonkAlex
Иногда (не всегда) бывает так, что старые сообщения уже не нужны. Есть русская пословица - "дорога ложка к обеду". Но люди упорно думают, что заказ пиццы это тоже самое, что и отправка денег. Нет, пицца в 7 утра (будучи заказанной в 11 ночи) никому не нужна.
Это вопрос бизнес-логики, а не технический. Если сервис получателя считает что сообщения старее N-минут или даже секунд - неактуальны, пусть сам их и "пропускает".
Пицца может и никому не нужна, но если деньги с клиента сняли, то как минимум надо решить, что делать дальше - возвращать деньги, привлечь внимание ответственных (письмо им выслать хотя бы) или что-то ещё. И исходное сообщение всё ещё может быть источником такой реакции.
Эти задачи были и решались и до популяризации брокеров сообщений.
Раскройте мысль. Это тред про "зачем нужна кафка" и про "синхронное\асинхронное взаимодействие сервисов".
Какие "эти задачи" вы имеете в виду?
В монолите очевидно можно обойтись без асинхронного взаимодействия, мы считаем что он целиком работает или нет. В микросервисах тоже можно, это вопрос требований к системе и построенной архитектуры. Асинхронность (и кафка как один из инструментов-вариантов реализации) нужна не везде, это просто ещё один инструмент, которым надо уметь пользоваться и про который надо знать.
В монолите очевидно можно обойтись без асинхронного взаимодействия, мы считаем что он целиком работает или нет.
Во-первых, не надо призывать к очевидности.
Во-вторых, монолит без асинхронного взаимодействия возможен, но, на практике, такое случается редко. В качестве примера, подавляющее большинство ERP систем - монолиты. Но взаимодействие между модулями/компонентами в ERP часто асинхронное. Просто вместо Kafka используются, например, средства Hana, где пакеты и очереди - обычное явление.
Асинхронное взаимодействие удобнее в части сценариев. А в части - это лишние накладные расходы, которых хотелось бы избежать.
В монолите есть возможность обходиться без асинхронных взаимодействий внутри самого себя. И я не призываю ни к чему, не надо кидаться в меня ссылками =)
ПС: и в целом текущий тред про какой то ряд задач, для которых очереди как раз не нужны, точнее могут решаться без очередей.
Асинхронное взаимодействие удобнее в части сценариев. А в части - это лишние накладные расходы, которых хотелось бы избежать.
Например, в парадигме SCM асинхронное взаимодействие - необходимость. В реальной жизни почти все события асинхронны и это не изменить.
Ну и по поводу лишних накладных расходов - Вы явно погорячились. Пакетная асинхронная обработка документов существенно выигрывает по производительности у синхронной подокументной обработки.
В монолите есть возможность обходиться без асинхронных взаимодействий внутри самого себя.
В теории. Но на практике, как только коснетесь реальных бизнес-процессов, без асинхронных взаимодействий, скорее всего, уже не обойдетесь. По крайней мере в моем опыте разработки, начиная с лохматого 1987 года, я еще ни разу не встречал монолит без асинхронных взаимодействий. Можете дать ссылку на более-менее сложный монолит на рынке без асинхронных взаимодействий между его модулями/компонентами?
не надо кидаться в меня ссылками
Не употребляйте в речи ключевые признаки демагогии - не буду )))
Например, в парадигме SCM асинхронное взаимодействие - необходимость
Только вот вы в треде, где наоборот человек спрашивает, зачем нужно это всё асинхронное, когда синхронно быстрее и лучше. Потому что разные задачи и разные вопросы. В долгих бизнес процессах будет асинхронность, любым способом, включая очереди. А в быстрых апи для клиента на фронте тыкающего кнопки - синхронность удобнее, если можем себе позволить. Но опять таки, всё сильно зависит от конкретных задач и бизнес требований в том числе.
Можете дать ссылку на более-менее сложный монолит на рынке без асинхронных взаимодействий между его модулями/компонентами?
И мы будем потом спорить, что есть что. Мне честно лень. Есть разные способы общаться, например может быть не очередь (которая про push модель), а апи для запроса данных (pull модель), что тоже асинхронность, но уже не очередь с точки зрения использования. И я видел и писал монолиты, где архитектура была на 90+% синхронной, а редкие асинхронные инструменты были по факту планировщиком\кроном. Т.е. инструмент был, но был неудобен и пристроен сбоку, что сильно ограничивало его использование. Да, это плохие (наверное) монолиты, но они есть и работают, так что это возможно.
Не употребляйте в речи ключевые признаки демагогии - не буду )))
Я не призывал ни к чему, а то что монолит можно написать без асинхронщины - для меня очевидно (и опять таки, если обсуждать это нормально, надо договариваться о терминологии, а мне лень). Если для вас не очевидно - то я не готов в это лезть.
А в быстрых апи для клиента на фронте тыкающего кнопки - синхронность удобнее, если можем себе позволить.
Даже в типовом случае, при заказе в интернет-магазине - уже не можем это себе позволить, так как резервирование товаров и оплата заказа - уже не могут быть синхронными. И это я еще не придираюсь к тому, что TCP, HTTP и веб-сервер - уже асинхронны по своей сути.
Потому и прошу ссылку на пример монолита на рынке без асинхронной обработки между его модулями/компонентами.
человек спрашивает, зачем нужно это всё асинхронное, когда синхронно быстрее и лучше
Я уже фактически ответил на этот вопрос: "Пакетная асинхронная обработка документов существенно выигрывает по производительности у синхронной подокументной обработки."
То бишь, асинхронная обработка в этом случае и быстрее, и лучше.
Странно слышать от программиста на C#, что синхронная обработка запросов может быть "быстрее и лучше", чем, хотя бы, штатная асинхронность с async/await. Вы ничего не перепутали?
И я видел и писал монолиты, где архитектура была на 90+% синхронной, а редкие асинхронные инструменты были по факту планировщиком\кроном.
В процессе разработки, на начальном этапе развития десктопного продукта - поверю. Но даже 40 лет назад вместо топиков Kafka активно использовались "топики" на магнитных лентах для асинхронной обработки. Сама архитектура IBM/360 была ориентирована именно на асинхронную обработку данных. Даже под однозадачным DOS активно использовалась асинхронная пакетная обработка в целях повышения производительности, как я уже указал выше.
для меня очевидно
Для того, чтобы какой-то факт стал очевиден, необходимо хотя бы привести ссылку на авторитетный источник, подтверждающий этот факт. Вы пока это сделать не смогли.
Странно слышать от программиста на C#, что синхронная обработка запросов может быть "быстрее и лучше", чем, хотя бы, штатная асинхронность с async/await. Вы ничего не перепутали?
Как программист уверенно заявляю - стоимость async-await может быть важной в этой схеме и действительно стоить перфоманса. Надо мерять, был опыт ухудшения времени отклика сервиса после перевода его на async-await.
Остальное обсуждать не буду, тред был не про это и в целом я с вами согласен.
ПС: а пакетная обработка не имеет никакого отношения к синхронной асинхронной обработке, но и пофиг.
был опыт ухудшения времени отклика
Опять? При чем тут время отклика, если речь шла о производительности всей системы?
пакетная обработка не имеет никакого отношения к синхронной асинхронной обработке
Смотря что Вы под этим подразумеваете. В обсуждаемом контексте ("Пакетная асинхронная обработка документов существенно выигрывает по производительности у синхронной подокументной обработки."), явно указана асинхронность. Если на пальцах, разноска (обработка) документа в этом случае выполняется асинхронно с действиями пользователя пакетом документов, созданных разными пользователями. Ну или, если ближе к разработке, сотня отдельных транзакций INSERT по одной записи от каждого клиента выполнятся на порядок медленней, чем одна транзакция INSERT сотни записей, сгруппированных со всех клиентов.
Опять? При чем тут время отклика, если речь шла о производительности всей системы?
Если что, мы в треде https://habr.com/ru/companies/selectel/articles/871012/comments/#comment_27733114 и автор комментария пишет "асинхронщина убивает перфоманс", что правда в моём субъективном восприятии, хватит кидаться ссылками на википедию. Конкретные сценарии надо профилировать, а не обсуждать на хабре. Что такое производительность всей системы - понятия не имею.
Пакетная асинхронная обработка документов существенно выигрывает по производительности у синхронной подокументной обработки
Вы этот контекст сами придумали и сами обсуждаете. А синхронность и асинхронность в контексте взаимодействия сервисов подразумевала (и мы вроде со всеми участниками друг-друга поняли) RPC в виде синхронности и очереди в виде асинхронности. Я не всегда правильно подбираю термины, возможно есть лучшие слова для того чтобы более ясно обозвать эти сценарии\ситуации.
"асинхронщина убивает перфоманс"
Асинхронная обработка изначально предназначена для повышения производительности. А то, что сдуру можно ей ухудшить производительность, так сдуру можно и член себе сломать.
Что такое производительность всей системы - понятия не имею.
Пусть у Вас есть сервис, который все ответы на запросы может выдать из своего кеша и на обработку запроса тратит 1 мс. Даже в случае локальной сети, среднее время ожидания ACK по TCP составит 2 мс. Если в сервисе не используется асинхронность, то эти 2 мс он будет сидеть в ожидании ACK в блокирующей записи и принять нового запроса не сможет. Издержки async/await в этом сценарии не превысят 0.03 мкс. Так как это на пять порядков меньше времени обработки запроса, то можем смело этим пренебречь. Итого, с async/await этот сервис даже на одном ядре может обрабатывать 1000 запросов в секунду, а без асинхронности - только 333 запроса в секунду. Так вот, предельное количество обрабатываемых запросов в секунду - это и есть производительность всей системы.
А синхронность и асинхронность в контексте взаимодействия сервисов подразумевала (и мы вроде со всеми участниками друг-друга поняли) RPC в виде синхронности и очереди в виде асинхронности.
Пока речь шла о сервисах я и не вмешивался. Я вмешался, когда речь зашла о монолитах. А в монолитах может не быть синхронности в виде RPC, но почти всегда есть асинхронность в виде очередей. Даже если, как 40 лет назад, эта очередь на магнитной ленте.
К слову, даже в сервисах, RPC - не всегда синхронное взаимодействие. Классический пример - асинхронный двунаправленный потоковый gRPC. У меня даже на текущем проекте уже больше десятка таких сервисов, которые ответ на второй запрос могут прислать раньше, чем на первый.
А в монолитах может не быть синхронности в виде RPC, но почти всегда есть асинхронность в виде очередей.
То, что в монолите было обращением в памяти, в микросервисах стало обращением по сети. Там, где монолит с одной БД мог использовать транзакционность этой самой БД, в микросервисах лучше использовать очередь для гарантий доставки.
Примерно это я имел в виду, когда писал "очевидно". Да, я мог написать это подробнее, но в целом я хотел понять, в чём вопрос был (и какие были задачи), но вы пришли и начали тыкать меня как котенка в лужу, хз зачем, когда у нас даже картины нет, о чём речь.
То, что в монолите было обращением в памяти, в микросервисах стало обращением по сети.
Вообще-то, в монолите с двухзвенной архитектурой клиенты общаются с СУБД тоже по сети и, нередко, асинхронно. В монолите с трехзвенной архитектурой по сети общаются как клиенты с сервером приложений, так и сервера приложений между собой, и сервера приложений с СУБД - всё тоже по сети и тоже, нередко, асинхронно. То, что в той же Hana происходят обращения в памяти, то это скрыто глубоко под капотом и является следствием её резидентности. Хотя с учетом того, что в Hana широко используются федеративные запросы к удаленным источникам данных, даже там немало обращений по сети.
Или многозвенную архитектуру Вы не относите к монолитам, называя монолитными только решения для мэйнфремов, где frontend, backend и СУБД выполняются на одном физическом кластере?
в микросервисах лучше использовать очередь для гарантий доставки
Можно об этом подробней? Если речь о Kafka, то он никак не гарантирует доставку. Если речь о RabbitMQ, то для значительных объемов данных он не подходит. На текущем проекте сейчас у меня очереди в Kafka, а подтверждение доставки или необходимости повторной публикации сообщений в очередь возложена на gRPC. Подписчик же может оперировать лишь своей точкой актуальности.
Если речь о Kafka, то он никак не гарантирует доставку
Гарантирует в пределах настройки длительности хранения. Это не СУБД (не стоит и не получится использовать кафку как хранилище), но можно указать хранение в условный год и сервис лежащий год в теории может встать и продолжить работу.
Гарантии примерно те же, что и у других очередей, насколько я знаю, не совсем понимаю чем аргументировать, есть например вот такая страничка https://docs.confluent.io/kafka/design/delivery-semantics.html
Подписчик же может оперировать лишь своей точкой актуальности.
А что с этим подходом не так? Я правда не совсем уверен, что такое "точка актуальности" - гугл подсовывает что-то из 1С и оперучёта, что в целом предметная область и к очередям не относится.
Гарантирует в пределах настройки длительности хранения.
Это и есть не гарантирует.
можно указать хранение в условный год
Можно, но обычно клиенты не хотят хранить терабайты сырых данных. Например, у меня около 100 тыс. вагонов на слежении и каждый час прилетает в среднем по две операции дислокации по каждому. Размер одной операции дислокации - чуть более килобайта в сжатом виде. Каждый час - более 200 МБ. Сейчас партиции обрезаны до недели и занимают около 50 ГБ. Если бы хранил год, то потребовалось бы 2.5 ТБ. Сетевые накладные чуть меньше, но так как они по всем грузовым вагонам на колее 1520, то неделя занимает 40 ГБ. Для года потребовалось бы 2 ТБ. И такого барахла, только из внешних источников, суммарно за неделю набирается свыше 0.5 ТБ. Как думаете, чтобы сказал клиент, если бы я потребовал у него вместо терабайта хотя бы в 30 раз больше серверных SSD под партиции Kafka? А ведь это я только потребности по Kafka только одной компании из всего холдинга клиента описал.
Гарантии примерно те же, что и у других очередей
Почитайте про RabbitMQ.
А что с этим подходом не так?
Сильно усложняет код. Если не отказываться от внешних ключей, то не сохраните в БД, например, операцию по вагону, для которого еще не пришел его паспорт или будете создавать для этого вагона фиктивный пустой паспорт. Хуже всего, если такой номер вагона уже есть, но сам вагон давно сгнил где-то в Дебальцево и такой же номер присвоили уже новому вагону. Если отказываться от внешних ключей, то усложняются выборки данных и требуются дополнительные усилия для поддержания консистентности. А в операции по вагону есть еще код станции, которой тоже может не быть в БД и информацию по которой нужно запросить из ЭТРАН. Там еще целая пачка аналитик, которые на момент получения операции могут быть неизвестны.
Накладные, дислокации, осмотры, ремонты, промывки, паспорта, различные справочные данные и многое другое - всё это разные топики, разные источники данных и разные микросервисы за них отвечающие. И за асинхронность поступления этих данных приходится расплачиваться.
что такое "точка актуальности"
Срез данных в БД до которого данные в ней консистентные.
Хорошо.
Смотрите, у меня есть система, в которой у меня синхронное (допустим) списание денег и асинхронное (условно) отгрузка-заказ товара у внешнего поставщика.
Когда пользователь кладет товар в корзину, мы создаем заказ со стейтом 0.
Когда пользователь оплатил (мы получаем колбэк от пеймент провайдера), мы обновляем стейт заказа до 1.
После этого инициируем внешний вызов (производства, склада, что угодно), синхронно. Обычно он проходит, меняем стейт на 4 (размещено у поставщика).
Нас же интересует ситуация, когда он синхронно не прошел (внешний сервис недоступен), меняем стейт на 3 (поставщик недоступен), записываем дату-время последней попытки, и инкрементируем счетчик сфейленных попыток.
--
Теперь по крону делаем N вызовов каждые M минут.
Если на какой-то из попыток успех - меняем на 4 (размещено у поставщика).
Как только количество вызовов или количество времени с момента размещения заказа превысило порог (количества попыток или количества времени), переводим в стейт 5 и инициируем возврат денег. Там уже условно 6 (пеймент провайдер недоступен, повторяем попытку спустя N времени) и 7 (колбэк успешного возврата денег от пеймент провайдера).
Обратите внимание, никаких очередей, работаем только со машиной состояний, асинхронные сабрутины работают через условный крон\шедулер, и либо живут внутри монолита, либо живут снаружи, но вызывают внутри монолита тот же код, что используется для "нормального" флоу.
У вас получилась классическая очередь в БД. Со всеми ее проблемами. Лет 10 назад так и писали, потом с радостью все это закопали и перешли на нормальные очереди.
Типичные проблемы вашего решения:
Сервис разбирающий по крону запущен на 10 инстанстах. И он хочет за раз брать десяток заказов. При этом любой инстанс в любой момент может отвалиться разными способами. Да-да, посередине работы тоже. Очередь заказов может быть большой, или даже очень большой. За час там нормально накопится, а для типичного магазина это приемлемая задержка.
Как решать проблемы синхронизации и импотентности всего этого добра будете? Я решал эти проблемы сам и видел чужие решения. Это все такое себе. Сейчас все перешли на нормальные очереди и начали переходить на вообще хороший Темпорал.
Темпорал - это в целом уже про процессы, как я понимаю. Ну т.е. мы уже не просто "сервис А сообщает сервису Б о событии В", а скорее "процесс события В подразумевает выполнение схемы Д на движке воркфлоу"?
Пытаюсь понять, в чём идея. Условно, сервис клиентов создал нового клиента по входному сообщению. Формат сообщения - ответственность сервиса клиентов, он предоставляет апи всем. И в конце делает широковещательное сообщение о появлении нового клиента (либо не широковещательное, а шлёт в конкретное место, ориентируясь видимо на входное сообщение). Внутри сервиса клиентов - у нас воркфлоу, можно на темпорал. А вот входное и выходное сообщения имхо уже не часть процесса и участники не должны знать и думать о существовании воркфлоу( Темпорал ) в сервисе клиентов?
Темпорал проще всего понять как "Распределенный планировщик". Это не совсем корректно, но так понятнее всего.
В нем ставишь задачу и он ее выполняет где-то когда-то. Он может распределять одинаковые задачи по нескольким воркерам, гарантировать надежность с учетом падений и все такое. В задаче может быть стейт. Небольшой, но в целом хватает.
Сейчас очереди нужны это если идет по настоящему большой поток сообщений. Темпорал плохо работает с миллионом задач в секунду. А если у вас немного, то Темпорал удобнее. Там все прозрачно и понятно.
Как минимум крон, который делает вызовы, должен знать что запрашивать. Я бы сказал, что он данные из очереди берёт. Будет это кафка-ребит или табличка в БД - часть реализации.
Тут в целом можно двумя способами:
Делать синхронный запрос для изменения в стейт 4. Если произошла ошибка - класть сообщение в очередь, крон из очереди разгребёт и переведёт в 4 статус в случае временных проблем, как сможет.
Сразу класть в очередь, крон разгребающий выполнит обработку когда доберётся до сообщения.
Но тут кстати классика очередей - а как делать переповторы? Можно либо повторно положить сообщение (с сохранением исходного времени, может со сдвигом счёткика повторов), либо заводить отдельные очереди под повторы (ну т.е. q1, q2, q3) и после условного q3 сообщение считать неуспешным, идти в ветку стейта 3 "поставщик недоступен". И всё это выглядит костылями в моей голове, но это то что я видел из работающих вариантов. Либо можно делать табличку в БД с той же логикой примерно, большой и критичной разницы не вижу.
UDP: и это я даже не вспоминал про вопросы выше от @BugM - обработку падений и параллельных обработчиков. Там на очереди из БД всё становится больнее.
Но тут кстати классика очередей - а как делать переповторы? Можно либо повторно положить сообщение (с сохранением исходного времени, может со сдвигом счёткика повторов)
Я вот примерно такую реализацию делал давным давно. Она до сих пор в проде. Работает - не трогай.
Кладем в табличку дату-время момента старта обработки. И рядом поле с количеством попыток. И только потом пробуем что-то обработать. Обработчик получает первые N записей с сортировкой по этой дате-времени. Так я получал те записи которые дольше всего не обрабатывались. И заодно было видно когда или время ожидания или количество попыток превышено и надо признать провал.
Там много всего еще было накручено, но не важно. Самое интересное это задержка если вторая попытка идет слишком быстро после первой. Повторять попытки чаще чем раз в десять минут нет смысла в моем кейсе.
мы запрашиваем что-то вроде
SELECT * FROM ORDERS
WHERE id_state = 3
ORDER BY createdAt ASC
LIMIT N;
и процессим их.
И 10 инстансов получат 10 одинаковых записей. Ну же, классика распределенных систем. Я такое даже на собеседование на сеньора спрашиваю на систем дизайне.
Вопрос со звездочкой. Вы сделали SELECT FOR UPDATE, а потом у вашего обработчика мигнула сеть до БД с локами и лок в БД пропал. При этом он вполне себе живой и продолжает работать. Что делать будете? Двойная обработка запрещена по бизнес логике. Обработчик у вас очень долгий и многопоточный. Углы срезать не выйдет, надо честно решать эту проблему.
Ну, допустим у меня в моей системе один инстанс который отрабатывает фейлы. Я все-таки предполагаю, что фейлов у меня относительно немного, и могу процессить их в 1 поток.
Даже если нет, я могу процессть только четные (нечетные) записи, только делящиеся на 3, и так далее.
Просто когда каждая пиццерия думает, что она Гугл, то стоимость разработки и поддержки ИС пиццерии улетает куда-то в облака, и оно в принципе даже работает, пока откуда-то в систему приходят деньги на это пиршество.
Если у вас в системе другие требования, то понятно, что она может быть спроектирована по-другому.
Так это решения не уровня пиццерий. Пиццерии надо купить CRMку подходящую и не париться.
Мы тут про большие серьезные решения. Когда один инстанс это вообще не решение. Всегда делается минимум по два всего. Чтобы сразу учесть типовые проблемы масштабирования, гарантировать хоть какую-то надежность и дать возможность катать релизы без даунтайма.
Во-первых, никто не запрещает всё выполнить одним SQL предложением или DO блоком.
Во-вторых, если уж очень хочется, то можно после SELECT ... FOR UPDATE проверять постоянно условие pg_current_xact_id_if_assigned() IS NULL и валиться, если это так. Например:
CREATE FUNCTION raise_error(code integer)
RETURNS integer AS $proc$
BEGIN
RAISE EXCEPTION 'User error %', code;
RETURN code;
END; $proc$ LANGUAGE plpgsql;
WITH IsInTransaction AS (
SELECT raise_error(12345)
WHERE pg_current_xact_id_if_assigned() IS NULL )
SELECT 'Something...'
WHERE NOT EXISTS (SELECT 1 FROM IsInTransaction T);
Есть еще ряд способов, в том числе и вообще без блокировок в БД и без SELECT ... FOR UPDATE.
Способов много. Они все сводятся к каким-то локам в любом централизованном хранилище и хартбитам этих локов.
Практика показала что это довольно сложный путь. Написать такой код корректно работающий во всех случаях в распределений системе мало кто может.
Поэтому все и отказались от такого кода и перешли на очереди. Где вообще ничего не нужно. Читай/обрабатывай, а остальное за тебя очередь сделает.
Практика показала что это довольно сложный путь.
А компромиссы с CAP теоремой простой путь?
перешли на очереди
И заработали массу проблем с консистентностью данных и распределенными транзакциями. И если бы проблема была только в блокировках внутри БД, то переход на очереди себя бы вряд ли оправдал.
Там не компромиссы. Там скорее переосмысление пайплайна обработки в новой парадигме.
Проблема в сложности таких решений и проблемах масштабирования. От них всех избавились разом, перейдя на очереди. Получил кусок данных и обрабатывай его независимо от всех. Как обработал скажи брокеру что готово и его можно удалить из очереди. Кусок для этого должен быть полностью независим от других кусков данных. И обрабатываться отдельно и независимо.
Стало удобно. Нет локов, нет синхронизации, нет хартбитов. Все понятно и просто. И новые обработчики чуть ли не мышкой добавляются в вашей системе управления контейнерами.
Там не компромиссы.
Расскажите, как добиться одновременно консистентности, доступности и устойчивости к фрагментации, вместо того, чтобы искать компромиссы. Это, в принципе, на Нобелевку может потянуть.
Получил кусок данных и обрабатывай его независимо от всех.
На практике, из десятков очередей получаете данные, которые так или иначе ссылаются друг на друга или зависят друг от друга. Но вот беда, мало того, что из одной очереди могут прийти данные ссылающиеся на данные в той, из которой эти данные еще не поступили, так какая-то очередь может лечь и взаимосвязанные данные из неё удастся получить еще не скоро. И вместо консистентности в конечном итоге получается консистентность только на некоторую историческую точку времени, которую еще надо корректно вычислить.
Я уже молчу о транзакциях, которые в микросервисной архитектуре - большая боль.
В результате, очереди используются лишь как транспорт для больших объемов данных, а вся основная логика по обеспечению ACID и консистентности падает на RPC.
Даже в небольших системах приходится создавать микросервисы для MDM и дергать их отовсюду по RPC. А в крупных системах, где MDM по объективным причинам отстает от данных приходящих по B2B каналам, для каждого микросервиса приходится индивидуально решать, что делать с неконсистентностью.
Много ли Вы знаете успешных ERP с микросервисной архитектурой на рынке? Я вообще ни одной. MS уже десять лет безуспешно пытается Dynamics 365 перевести на микросервисную архитектуру. SAP по прежнему рекомендует использовать XS в Hana, вместо реализации логики снаружи. OEBS даже не пытается сменить архитектуру. Infor, Epicor, IFS, Sage - тоже никак. Не задумывались, почему так?
Так вы не делайте десятки зависимых очередей. Над потоками данных надо подумать и сделать нормально. Это не наследие 20 летнего кода, это все довольно свежие решения.
Любой поток должен быть независим от других в идеале или зависимость должна быть простая и понятная в реальном мире. Вон те идут первыми и эти строго после, проверить те дошли или нет можно простым запросом куда-то. Желательно проверять даже не для конкретного пакета, а глобальный таймер что обработано получать. Но тут по разному выходит.
Никаких ссылок на данные из другой очереди быть конечно же не должно. Ссылки на БД могут быть, это нормально.
ERP не писал, тут я пас. При это другие большие системы писал и это хорошо работает, но подумать при проектировании стоит крепко. И обсудить хотя бы в узком кругу что надумал. Ревью это хорошая штука.
Так вы не делайте десятки зависимых очередей.
Любой поток должен быть независим от других в идеале или зависимость должна быть простая и понятная в реальном мире.
Как раз в реальном мире простых и понятных зависимостей нет. Я уже приводил пример выше. Если к каждой операции той же дислокации вагонов я прицеплю накладные, осмотры, ремонты, промывки, паспорта, натурные листы, различные справочные данные и многое другое (только по АСОУП-3 - это свыше 40 видов документов и около 300 видов НСИ), то вместо 1 КБ у меня каждая операция станет занимать больше мегабайта. А размер партиций Kafka на неделю только для дислокаций вагонов потребует вместо 50 ГБ свыше 50 ТБ.
Ссылки на БД могут быть, это нормально.
Ссылки на БД, которая пополняется из тех же очередей. Пришли к тому же, с чего начали.
ERP не писал, тут я пас. При это другие большие системы писал и это хорошо работает, но подумать при проектировании стоит крепко.
Вот и подумайте, например, как считать детализированный COGS в микросервисной архитектуре. Особенно с учетом необходимости корректировок в нем по всей цепочке поставок, производства и сбыта. Сразу расхочется такие объемы перекрестной информации, экспоненциально возрастающие с ростом количества участвующих в этом микросервисов, гонять по сети. Какие я не видел попытки реализовать ERP на микросервисной архитектуре, так или по центру всё равно возникает финансовый монолит, или нет детализированного COGS для производства.
Я как и другой ваш собеседник тоже выйду из дискуссии. Вы слишком пошли в бизнес логику в которой я ничего не понимаю и не особо горю желанием разбираться.
Приходите с обычными абстрактными примерами, чтобы никому не пришлось погружаться в вашу бизнес логику. Магазин, склад и тому подобное без ненужных деталей.
Я вот работаю с неизменяемыми данными. То есть они в принципе неизменны. Можно только дописать что-то, удалить нельзя ничего. На исправление и дописывание есть определенные окна, если проехали то все. Вообще все. Там тоже много специфики. Но я никого ей не гружу.
Пока у вас одна нода\один инстанс выполняющий обработку - в целом сойдет, главное чтобы были гарантии что инстанс строго один.
Обычно, когда начинаются проблемы на нагрузке - вот конкретно такие решения и приходится либо велосипедить, либо переделывать на очереди (которые не закроют проблему целиком, но хотя бы имеют типовые решения и их можно загуглить).
ура-ура, что сходит хайп. 15 лет назад все носились с gof patterns, как с торбой писаной, теперь на собеседованиях ждут пересказа букваря про MSA. Причем пересказа дословного, если говоришь свое мнение, которое не на сто процентов совпадает с "мнением команды", то, как писал камрад выше, тебя гонят сцаными тряпками, даже если обратная связь по техническому интервью - exceed expectation. Вот и продолжаем дружно натягивать сову на глобус глаголами протокола, который был разработан десятилетия назад для простых задач вопрос-ответ при мендленных соединениях.
Хочу отметить, что в тех самых транснациональных корпорациях, где сервера по всему миру, такого секстанства не наблюдается. Там и шины, и сервисы(не микро) и даже телнеты в зеленый экран на хост, который может физически находится в другом континенте.. С другой стороны, в таких корпорациях потребитель информационных ресурсов внутренний, а в гуглах, ибеях, реддитах - внешний. До сих пор для меня загадка, зачем в суровом корпоративном ИТ необходимость "быстрой выкатке" изменений и зоопарк из команд, технологий и скиллов, которые превращаются во что-то монстрообразое. Бюджеты осваивать? : )
Хорошо, но как без таких коммуникаций обходиться-то?
Не обязательно быть прямо таки корпорацией - растущий стартап тоже получает преимущества. Когда, например в этом году охват 5 стран, а в следующем уже 20.
Кто об этом задумывается? Мало кто. А деньги то горят там по чуть-чуть.
В корпорациях, благодаря высокой культуре DevOps, каждый разработчик видит, сколько его сервис/БД потребляет в деньгах каждый месяц.
Придумали какие то "микросервисы" все уже было сто лет назад, com/dcom, corba.
Я не понял принципиального отличия Монолит 2.0 от концепции пятидесятилетней давности, одна VM на одно модульное приложение в VM/SP
Когда смотришь на эволюцию архитектур, понимаешь, что технологии всегда движутся по кругу.
По спирали, а не по кругу. И это не случайность. Это 3 закон диалектики от Гегеля.
Он проявляется везде. От государственного и общественного устройства до бытовых моментов.
Звучит он как "отрицание отрицания". Смысл в том, что история всегда идёт по спирали. Переход к новому витку идёт через отрицание старого. А старое когда-то пришло через отрицание ещё более старого уклада. Вот и получается отрицание отрицания.
Также этот закон говорит о том, что все новое, даже возникнув в результате отрицания прошлого, будет использовать этот опыт в качестве основы для своей эволюции.
В случае архитектур следует ожидать новых монолитов, взявших многое от структур микросервисов. Кстати, это даже по комментам видно:
Придумали какие то "микросервисы" все уже было сто лет назад, com/dcom, corba.
Или вот
Я не понял принципиального отличия Монолит 2.0 от концепции пятидесятилетней давности, одна VM на одно модульное приложение в VM/SP
Уйдем. Ибо нефиг...
Serverless подход (лямбды в облаках) сочетает удобство монолита с масштабируемостью микросервисов (во всяком случае под нагрузки 99% среднестатистических приложений). Видимо люди не доверяют облакам (хотя можно и без вендорлока делать)
Просто это дорого. Облака дерут в три конца.
И по безопасности вопросов масса.
Ну сами лямбды кстати не сильно дорогие. Dev/QA лабы можно на каждый чих создавать и не париться что деньги будут за время существования браться, как в случае контейнеров. Так что их можно рассматривать как минимум при выборе контейнеры в облаке vs лямбды в том же облаке, когда косты на всё остальное плюс минус будут одинаковыми.
Я думаю тут, как и везде, нужно считать. Нужно понимать как именно строить арихтектуру и какие сервисы нужно использовать, чтобы было дешево. Для некоторых типов задач решение может быть крайне не очевидным.
Для сравнения, у моей команды суммарная стоимость на несколько продуктов (prod + dev каждый) выходит около $4k в месяц. У нас не дикий RPS, но сотня тысяч уникальных пользователей в месяц есть. Сравнимо с зарплатой, которую пришлось бы тратить на девопса, чтобы это поддерживать (уж не говоря про то сколько стоило бы само железо).
По моему мнению, serverless выходит дороже только в двух случаях:
Ваш проект настолько маленький, что умещается на одном VPS. Тогда можно просто купить инстанс (или хостить контейнер в Fargate) и это будет дешевле.
Ваш проект настолько большой/высоко нагруженный, что свое железо действительно получается дешевле даже с учетом всех накладных расходов. Но мне кажется, что это прям уровни всяких авито/озонов/и т.д.
удобство монолита
А в чём удобство распределенного монолита? Если разные лямбды ходят в одну базу и используют общий код, то там такое веселье с консистентностью доступа к данным и апдейтов самого кода начинается.
Бегло почитал про лямбды в облаках... На первый взгляд похоже на перерождение PHP: процесс собирается с нуля, выполняется и умирает. )
всегда считал, что микросервис - это возможность разбить что-то на мелкие модули и масштабировать только бутылочные горлышка. Но при этом разработчик, точнее архитектор, может заранее просчитать где будут бутылочные горлышка и микросервисами покрывать только потенциально высоконагружаемые части, остальное реализовывать обычными сервисами /монолитами.
Вся суть микросервиса - в том, что масштабировать малое можно дешево.
А в чем вы видите проблему горизонтально масштабировать монолит?
Если монолит такой большой, что прогретый процесс требует 32+ гига оперативы, то масштабировать такое горизонтально будет сильно накладно.
А некоторые, при запуске монолита, делают долгоживущие подключения в базы и в другие сервисы, которые множить тоже нежелательно.
Но для небольших (относительно) монолитов, с ленивой инициализацией модулей и подключений - почему бы и нет, вполне рабочее решение.
Про память это условно, всё зависит от особенностей приложения, если у вас 32гб монолит, но масштабируетесь вы потому, что у вас на один эндпоинт, который потребляет при вызове 100 байт, миллион запросов в секунду, а остальное приложение просто пассивно висит в памяти, то да действительно скейлить всё приложение только ради одного эндпоинта странно и его нужно выносить. Однако если у вас нагрузка +- гомогенна на всё приложение, то распилив эти 32 гигабайта по микросервисам вы не получите преимуществ с точки зрения памяти, а скорее наоборот, поскольку помимо полезной нагрузки отдельные рантаймы будут потреблять память в то время как в монолите рантайм потребляет память один раз.
Что касается ресурсов, например коннекшенов, то здесь я соглашусь, за этим надо внимательно следить при масштабировании монолита, это может вызвать проблему, особенно если внешних ресурсов много и каждый из них используется только одним модулем. С другой стороны, например, если у вас подключение к одной базе, используется connection pool и модули работают с разными схемами, то как и в случае с памятью распилив приложение вы не получите преимуществ, а скорее наоборот.
прогретый процесс требует 32+ гига оперативы
Тогда этот кеш надо выделить в отдельный модуль и назвать его Redis
Давно-давно было такое. Вроде умели как бы не с 80тых. Но в реальной реализации монолитов были иногда проблемы. Примерно в середины десятых все адекватные их решили.
Смотря что за цель приследуется при масштабировании. Если обеспечить HA - то особой проблемы нет (но бывают нюансы), если для распределения нагрузки, то этот монолит должен быть с нуля написан так, чтобы такая возможность имелась. Иначе не выйдет. Обычно масштабировать монолит мешает:
Эксклюзивный подход в работе с БД, транзакциями и, собственно, самими данными
Способ кэширования (и инвалидации)
Другие механизмы роутинга и механизмы вызова бизнес-логики
Долгий старт и инициализация монолита
Непонятно, если создается такой продукт с такими ограничениями на эксклюзивность (т.е. не допускается работа 2 инстансов), то как такое тогда обновлять? Ведь в момент обновления старая версия должна отработать полученные запросы и в то же время новые запросы уже идут работают с новой версией. Просто грубо вырубаете сервис (огребая кучу 5**) или дропаете трафик - типо у пользователя тормозит?
Да. Именно так:
поднимаем новое рядом. как поднялось
переключаем роуты и дропаем старое
Большинству сервисов/приложений этого более чем достаточно. Даже downtime в 5 минут на перезапуск им не сделает погоды.
Требование к высокой доступности, на самом деле, мало кому нужно. А если так, то зачем платить больше?
Большинству сервисов/приложений этого более чем достаточно. Даже downtime в 5 минут на перезапуск им не сделает погоды.
Это несколько сомнительно. Мы например на основном проекте делаем в среднем 2 (иногда до 5) выката в день. Если бы мы лежали в день 10-25 минут, пользователи точно не были бы в восторге (а если еще учесть контекст сервиса... биржа на которой удут торги). Кажется что никакому реально работающему сервису его пользователи не простят такого простоя. Смещать простой на ночь - это тоже не бесплатно. Эксплуатация не обрадуется и запросит или больше золота или юнитов.
А в чем вы видите проблему горизонтально масштабировать монолит?
В общем случае, монолит жрет больше ресурсов, поэтому его масштабировать дороже.
Приведите, пожалуйста, пример. В общем случае это не так и зависит от платформы. Если у вас есть монолит, например на java, который потребляет 8Гб памяти, то распилив его, суммарная потребляемая память увеличится, поскольку для каждого микросервиса помимо полезной нагрузки будет подниматься отдельный jvm рантайм, потребляющий память. В случае монолита, такой рантайм только один.
Shopify
Насколько помню, у них был доклад на конференции, в котором они поделились наблюдением, что узким местом у них оказался вовсе не приложение (которое монолит на Ruby), а база данных. И за счёт шардирования БД по подам (с несколькими клиентами на каждом) они начали лучше справляться с нагрузкой.
И так будет у 90%. Причем база будет забиваться на чтение. Т е добавление slave-реплик может решить проблему. А люди из-за этого хайпа огромные ресурсы тратят... Я помню с чего началось: появился ряд людей, которым "всë не нравится" (Такие есть всегда и везде, особенно в разработке) И выдвинули ультиматум ( именно ультиматум), что мол им не нравится, что они приходят в организацию, а там все пишут на яве (можно подставить что угодно - питон, перл, пхп, го, раст, эрланг) и используют один стек. Мы де, хотим свою уютную команду и свой уютный стек. И понеслось!
Этот подход начали всячески оправдывать, назвали микросервисами. Да, именно так! Именно это тогда выдвигалось, как главное преимущество! Я помню времена как хвастались, что у них в компании 10 разных стеков и "всë работает". Я тогда смотрел на этих шизиков и удивлялся. Потом как-то постепенно эту мантру, что все пишут на чем хотят, начали из списка преимуществ микросервисов выкидывать... Потому что преимущество сомнительное. Рациональность свое начала отвоевывать.
Потом стали говорить " Ой, да мы с новой архитектурой по 50 релизов в день делаем! " Тоже так себе идея. Во первых разрабы только и занимаются релизами, во вторых а что мешало с монолитом делать тоже самое? Явно не то, что его исполнимый файл больше весил. Значит бардак был в архитектуре. И тут в улучшение наконец вложили денег. Причем нехило так. Распил монолита - это не пять минут. А насчет количества релизов - так тоже бардак, но только организационный. Ваши команды не в состоянии скоординироваться и сделать релиз, например раз в три дня.
И вот только теперь всерьез говорят, а что собственно потерялось с переходом на микросервисы ? А потерялось дофига: нормальная транзакционность, нормализация данных, простота разработки (серьезно кто-то до сих пор думает, что с учетом первых двух пунктов разрабатывать микросервис проще монолита?), легкость тестирования, и не очевидное - надежность и детерминированность поведения (относительная, при условии что бд работает. Выше писали, полностью с этим согласен)
Ну и последнее: а чем на самом деле лучше микросервисы? Иными словами это вопрос, а зачем они нужны? Рациональное логическое мышление дает ответ: это отделение контекста исполнения, т е очень жесткий барьер, который при падении одного контекста, предотвращает падение соседних. Вот в такой трактовке микросервисы будут надежность повышать. Т е у нас есть некий критический компонент, который не должен стать не доступным. При этом к остальным частям системы таких требований нет. Мы этот компонент отделяем в отдельный контекст и дуплицируем, делаем облако. Ну для примера: компонент авториции и аутентификации. Критический, т к его используют все части системы. И при его недоступности всë остановится. Значит как то нужно обеспечить его надежность. Можно дублировать весь монолит, но тогда при его росте могут возникнуть проблемы различного рода. Плюс в этом случае требования надежности предъявляются к нему целиком. А это может всë сильно усложнить. Но вот тогда принимается решение вынести в отдельный сервис эту функциональность. (Я подозреваю, что очень много будет не согласных с таким подходом. Но что ж, у всех есть возможность свою точку зрения изложить... )
Подпишусь под словами что можно и нужно выделять общие сервисы без которых работать нет смысла и ходить в них синхронно.
Но я бы не хотел разрабатывать такой сервис. С такими требованиями по надежности там любой чих будет обложен парой регламентов, а релиз будет катиться месяц.
У микросервисов есть вполне осязаемые технические плюсы.
В классических микросервисах только один сервис владеет базой и никто другой.
Отсюда преимущества:
лёгкость изменения индексов, структуры и прочего одной командой.
возможность грамотно и надёжно кешировать данные в памяти микросервиса
Ну и очевидны недостатки связанные с БД: проблемы с консистентностью, транзакциями и перформансом.
Кроме того, микросервисы изолированы друг от друга, а значит меньше проблем с конфликтом библиотек.
Что же касается надёжности, то микросервисы в этом наоборот проигрывают
Да, микросервисы гарантируют что взаимодействие будет только через контракты) апи.
Но нормальные языки как C#, Java, частично Rust могут гарантировать изоляцию модулей и взаимодействие только через стабильнее контракты.
Вот PHP, Go, Python - те наяривают на микросервисы и расхваливают потому что отродя слаще не сдали.
Из моих личных столкновений с монолитами и микроархитектурами в контексте телекома, использование монолитов сильно экономит место в стойках. По мимо места в стойках сетевая обвязка становится более простой и дешевой. Сказать что микросервисы дают какието реальные бонусы как клиету так и сетевому администратору нельзя. Вот у тебя железяка которая у тебя абонентский шлюз, а вот у тебя стойки на которой наверчен опенстэк, где на серваках живет абонентский шлюз в виде фиговой тонны машин, причем иногда такой же почти 1в 1. Когда у тебя чтобы собрать несколько каких нибудь pgw на микросервисах просят 10к ip адресов чтобы сшить всё это микросервисное чудо, думаешь "чтоже с тобой делать когда в тебе чтото сломается". Монолит в виде коробки тем временем, подключай к питанию, к фибрике 10 минут на залив конфига и ты в деле.
Я нашел для себя лучший вариант и компромисс - это микросервисный монолит на Эликсир. Жаль только этот стек достаточно экзотичен. Очень недооценен.
К примеру Next или любые другие fullstack фреймворки - это старый PHP, но намного неудобнее и даже медленнее из-за некой сложности конфигурации, так как PHP прямо-таки подточена под эту работу.
Опять сравнивают Uber и прочий eBay с мелкими сайтиками на 100 человек в день с точки зрения архитектуры.
В первую очередь количество отдельных сервисов у больших компаний связано с тем, что они работают на куче национальных рынков и должны соблюдать требования законодательства отдельных стран. Грубо говоря, Амазон в Европе и Амазон в США это два разных Амазона. Например, по расчету сроков/стоимости доставки проще выкатить отдельный сервис для каждой локации и собрать туда все тарифы и сделать интеграции с локальными доставляторами, чем городить глобального монстра. Ну и по администрированию, я думаю проще менеджеру в Испании разбираться со службами доставки в Испании, чем централизованно согласовывать все это.
к примеру Rails, в последней версии отказались от Redis. Теперь вебсокеты очереди и кеш основаны на БД. ещё и SQLite в продакшн. Думаю самый дешёвый монолит в плане обслуживания. По рассказам в просторах инета выдерживает довольно большие нагрузки.
А чем редис-то им плох для очередей и кеша?
Потому что использовать БД в качестве кеша имеет смысл только, если оригинальный источник очень медленный.
Подождите. Редис это как раз не БД, а key-value storage. А вот они перешли с него на БД.
возможно чтобы снизить затраты на оперативную память сервера.
ну и хранить кеш сколько угодно долго.
Редис - это БД хранящая key-value. И надо сказать перформанс там иногда весьма хромает. В моей предыдущей компании редис был один из главных источников тормозов (не из-за базы самой, а её неверного использования)
Я правильно понимаю, что вы называете Docker и Spring Boot атрибутами некой концепции Монолит 2.0?
Они уже лет 6-7 являются стандартом микросервисной архитектуры и созданы специально как инструменты для микросервисной архитектуры. Никакого отношения к монолиту они не имеют.
Один знакомый архитектор говорил что ему нравятся микросервисы. И производительность команд только растёт и тестировать легче. А размер такой чтобы написать его за 2 недели.
Потом его, конечно, в дурку забрали.
Правда, на PHP или python нет хороших способов стабилизации контрактов, что необходимо для модульного монолита. Зато в C#, Java есть.
🌟Какие положительные качества микросервисов мы хотим перенести в модульный монолит?
✅ Чёткие границы модулей и интерфейсов
✅ Независимая разработка и лёгкость интеграции
✅ Чётко разделённые зоны ответственности
✅ Обработка ошибок и устойчивость (resilience)
✅ Удобный мониторинг, логирование и наблюдаемость
✅ Независимое и простое тестирование
✅ Адекватная и ограниченная гибкость по выбору технологий внутри модулей
При грамотном применении микросервисов мы часто наблюдаем следующие преимущества:
1. 🔸 Ясные границы и чёткие интерфейсы между компонентами
Микросервисы заставляют явно разделять области ответственности (bounded contexts), имеют отдельные API (REST, gRPC и пр.).
✅ Полезно для монолита:
Ясно выделенные и задокументированные API модулей.
Жёсткое соблюдение границ контекстов, никаких неявных зависимостей.
Компоненты имеют чёткие границы ответственности.
2. 🔸 Частичная независимость разработки и развертывания (изолированность модулей)
Микросервисы независимо деплоятся и масштабируются.
✅ Полезно для монолита:
Лёгкая независимая разработка и тестирование модулей.
Слабо связанная сборка (loose coupling).
Ясные интерфейсы и стабильные «клиентские контракты» (interfaces / abstractions API).
3. 🔸 Четко разделённая ответственность и ownership
Микросервисы помогают ясно выявить кто и за что отвечает.
✅ Полезно для монолита:
Ясное сопоставление модулей и ответственностей отдельных команд/людей.
Каждый модуль имеет ответственного (maintainer) и «зону ответственности».
4. 🔸 Автономные решения по стеку технологий внутри сервиса
Микросервисы позволяют разным сервисам выбирать наиболее подходящие языки, технологии и подходы.
✅ Полезно для монолита (умеренно):
Гибкость на уровне выбора библиотек, инструментов и фреймворков внутри модулей (но, конечно, в рамках одного или небольшого набора совместимых языков)
Сбалансированный выбор технологий, но без бесконтрольного разнообразия.
5. 🔸 Высокая независимость отказов (resilience)
Отказ или ошибки одного микросервиса не должны «заваливать» всю систему.
✅ Полезно для монолита:
Модули должны адекватно обрабатывать ошибки, не вызывая каскадные поломки.
Надёжность и стабильность модулей ("fail fast", обработка и пробрасывание ошибок, fallback-решения).
6. 🔸Простота мониторинга, логирования, трассировки
Микросервисы вынуждают строить удобные системы мониторинга и управления наблюдаемостью, распределённый трейсинг, метрики.
✅ Полезно для монолита:
Прозрачный мониторинг, метрики и трассировка на уровне модулей.
Единое понятное логирование с чётко идентифицированным источником сообщений.
7. 🔸 Лёгкость независимого тестирования и интеграции
Микросервисы существенно облегчают независимое (юнит- и интеграционное) тестирование.
✅ Полезно для монолита:
Модули легко тестируются отдельно.
Модульные и интеграционные тесты пишутся и хорошо поддерживаются.
🛠️ Кто должен обеспечивать эти качества в модульном монолите: архитектор или команда разработки?
На самом деле, успешное внедрение качеств микросервисов в монолит невозможно без совместной работы архитектора и всей команды. Однако чётко обозначим зоны ответственности:
🎯 Ответственность архитектора (или ведущего разработчика, тимлида):
Создание целостного дизайна архитектуры, разделение на модули и чёткое определение интерфейсов.
Формулировка единых правил архитектуры и tech-vision по структуре и взаимодействию модулей.
Создание и поддержание архитектурной документации и системы принятых решений (RFC, ADR).
Контроль соблюдения стабильности контрактов и границ модулей, предупреждение архитектурного долга на уровне крупной структуры.
🎯 Ответственность команды разработки:
Соблюдение и реализация четко сформулированных правил архитектуры.
Поддержка внутреннего качества модулей (чистота кода, покрытие тестами, документация API).
Поддержание надёжности и обработки ошибок внутри модулей.
Ответственность за соблюдение архитектурных соглашений, внутренняя культура код-ревью в команде.
📌 Распределение зон ответственности кратко:
🌐 Архитектор команды 🔧 Команда разработки
🔸 Глобальная структура и разделение на модули 🔹 Качество кода внутри модуля
🔸 Правила и соглашения по взаимодействию 🔹Внутренние тесты модулей и стабильность реализаций
🔸 Архитектурная документация 🔹Документация интерфейсов модулей
🔸 Контроль серьезных архитектурных решений 🔹Соблюдение дисциплины разработки и реализации
🔸 Направление технологического развития 🔹 Локальные выборы инструментов и практик в рамках согласованной политики
🎯 Итоговый вывод:
Большинство преимуществ микросервисного подхода можно и нужно переносить в монолит при построении модульного монолита.
Архитектор отвечает в первую очередь за внешние границы, интерфейсы, правила взаимодействия и global view архитектуры.
Команда обеспечивает локальное качество реализации модулей, соблюдение архитектурной дисциплины и чистоты кода внутри этих границ.
Только совместная работа архитектора и всей команды позволит вам реализовать лучшие качества микросервисов, сохраняя удобство управления и преимущества единого процесса модульного монолита. 🌟
Будущее микросервисов: уйдем ли мы к монолитам 2.0?