Comments 35
Пример 1 — вы создаёте сервис, они создают сервис, все создают сервисы
А можно подробнее как для данного примера поможет "монолит"?
Самая большая сложность, со слов моих собеседников, заключалась в обслуживании всех этих микросервисов — отслеживании устаревших зависимостей, версий среды выполнения, отсутствии понимания внутреннего устройства некоторых из сервисов и так далее.
Вроде как основная идея инкапсуляции как раз в том, что если что-то работает как надо это вполне можно расценивать как чёрный ящик и не лезть туда.
На самом деле, какой-то крупный монолитный монолит - это (на мой взгляд) некая страшилка типа бабайки под кроватью (свесишь руку с кровати - бабайка откусит).
Куда более распространены т.н. "модульные монолиты", которые и не монолиты вовсе а набор модулей, каждый из которых и есть тот самый "черный ящик", выполняющий свою атомарную логическую функцию. Фактически каждый модуль суть процедура, оформленная в виде отдельного бинарника (со всеми вытекающими - отдельно разрабатываем-отлаживаем-тестируем-разворачиваем, в любой момент можем изменить внутреннюю логику при условии сохранения обратной совместимости контракта и т.п.)
Вообще все это восходит корнями к модели акторов А уж модульный монолит, микросервисы... Это тонкости реализации. Везде есть свои плюсы и минусы. Основной минус микросервисов, как мне кажется, в их чрезмерной изолированности. Например, если у вас есть БД из десятков тысяч объектов и все сервисы так или иначе их совместно используют, то держать копию БД на каждом сервисе - огромные проблемы с синхронизацией и репликацией. Опять же передача данных между сервисами через джейсоны (да даже протобуфы) тащит за собой большие накладные расходы (особенно если речь идет о hiload системах с десятками тысяч взаимодействующих между собой сервисов).
И тут опять выиграет "модульный монолит" при условии что каждый модуль, будучи обособленным бинарником, может вызываться как обычная внутренняя процедура с передачей описанного контрактом набора параметров as is (т.е. ровно так же, как вызывается любая процедура).
Про модули не совсем согласен. Но, в целом, Вы правы. Микросервисы это не что-то, к чему стоит стремиться. Это очень, очень плохая архитектура. Просто, она лучше, чем смерть проекта. Аналогия, которая на ум приходит, это https://ru.wikipedia.org/wiki/Маляриятерапия
Я, наверное, не стал бы называть ее абсолютно плохой. Вполне допускаю, что в каких-то случаях она будет наименьшим злом. Но хайп вокруг микросервисов явно неоправдан.
Сам работаю с "модульным монолитом". Наш стек позволяет у любой программе иметь набор аргументов не фиксированный, как в С (int argc, char* argv[])
но произвольный - как у любой процедуры. Фактически, процедура может быть внутренней (в той же программе) или внешней - внутри динамичекской библиотеки (extproc
) - это случаи раннего связывания, или вообще отдельной программой (extpgm
) - позднее связывание.
И вот как пример. Есть такая сущность у нас - дата актуализации клиента. Которая берется из таблицы. Причем, таблица эта - "историческая" - хранит всю историю изменения этих самых дат актуализации. И чтобы получить текущее значение баты актуализации нужно по этой таблице взять самую свежую (с последней датой изменения) запись. Для этого был реализован ретривер - программа GETDA1R
для вызова которой она (в вызывающей программе) описывалась как "внешняя процедура"
Dcl-PR getDatInfo extpgm('GETDA1R');
CUS char(6) const;
CLC char(3) const;
Typ char(3) const;
dsHDA likeds(t_dsHDA);
dateOnly ind options(*omit: *nopass);
Error char(37) options(*nopass);
End-PR;
на вход которой передается идентификатор клиента (CUS + CLC
), тип даты актуализации (Typ
) и структура для заполнения нужной информацией из найденной записи dsHDA
Dcl-DS t_dsHDA len(512) qualified template;
DAT zoned(7:0) inz(0);
USID char(4) inz(' ');
UNAM char(35) inz(' ');
BRNM char(4) inz(' ');
BRN char(35) inz(' ');
CRD timestamp inz(*loval);
MBN char(2) inz(' ');
MBND char(35) inz(' ');
SQN zoned(3:0) inz(0);
End-DS;
Ну и пара необязательных параметров...
Т.е. внутри вызывающей программы все это вызывается как обычная процедура, например:
getDatInfo(CUS: CLC: 'DOC': dsHDA);
При вызове getDatInfo
на самом деле будет вызвана программа GETDA1R
.
Таких вызовов огромное количество во всей системе. В самых разных модулях.
В какой-то момент решили улучшить производительность и сделали "витрину" - отдельная таблица, где хранятся только текущие значения (т.е. не надо искать "самую свежую запись" - просто берем запись по уникальному ключу CUS + CLC + TYP
и все.
Для этого просто переписали сам ретривер. С сохранением контракта. Все. Во всех 100500 местах где оно вызывается стало работать быстрее. Без отслеживания - где вызывается, как вызывается. Контракт сохранился - все работает. Просто заменили один модуль.
Тот самый "черный ящик", выполняющий атомарную логическую операцию.
Поддержу
Надо попробовать на каком-то проекте вернуться к "монолиту", потому что конечно "интересно", но иногда думаешь - а как бы оно все было проще :)
Вообще то одна БД на кучу сервисов - это антипаттерн для микросервисов.
Если у вас что-то такое, есть распределённые транзакции, etc., то очевидно имеет смысл использовать другие инструменты.
Да, об чем и речь.
А нас (АБС банка) порядка 30 000 объектов БД. И несколько десятков тысяч различных программных модулей, которые эти объекты использует одновременно, реализуя различную бизнес-логику. Объекты БД, которые используются одним-двумя модулями, конечно, есть, но таковых крайне мало (обычно это какие-то рабочие таблицы, не для постоянного хранения данных). А есть ряд таблиц (например, основная таблица клиентов), которые используются практически везде (в том или ином виде).
Понятно, что микросервисная архитектура тут приведет к очень большим накладным расходам.
Вообще, ближе всего, все-таки, модель акторов. Есть некоторое количество сервисов, которые работают постоянно. Например, отслеживание очередей сообщений. Как только в очереди появляется сообщение, сервис определяет его тип и по своим настроечным таблицам находит обработчик сообщения для данного типа и запускает его, передавая ему на вход тело сообщения.
Аналогично работают т.н. "журнальные мониторы" - есть сервис, отслеживающий изменения в таблицах по системному журналу. При обнаружении изменения в журналируемой таблице сервис находит (по настройкам) связанные с этой таблицей ЖМ и запускает их с передачей на вход образов записей "до" и "после" и типа изменения (удаление/добавление/изменение) записи.
Т.е. система когда событие инициирует связанный с ним актор.
Основной минус микросервисов, как мне кажется, в их чрезмерной изолированности.
А что мешает использовать в нескольких микросервисах общую библиотеку? Это противоречит определению микросервиса? А сколько есть определений микросервисов? Какому проценту этих определений противоречит использование одной библиотеки несколькими микросервисами?
По моему "чрезмерная изолированность" это ошибка конкретного разработчика-архитектора в конкретном проэкте. Даже тот же самый архитектор вполне способен с некоторой вероятностью избежать этой ошибки в своем следующем проекте... если он, конечно, в состоянии учиться на своих ошибках и если он действительно архитектор. Я так думаю.
Что такой "общая библиотека" (и вообще "библиотека") в данном контексте?
Я не против микросервисов как таковых. Просто есть много вариантов реализации "модульности". И каждый имеет свои особенности и уже от системы и задачи зависит как именно реализовать тот набор требований (модульность и независимость разработки-отладки-тестирования-развертывания для каждого модуля) к системе.
Что такой "общая библиотека" (и вообще "библиотека") в данном контексте?
В каком "данном" контексте? Я не вижу чтобы кто-то внес хоть какую-то определенность с контекстом. Это я вас спрашиваю что вы понимаете под микросервисом, в зависимости от этого будем анализировать контекст. Сначала надо определиться что такое микросервис (почему не нано-сервис, милли-сервис).
У меня почему то складывается впечатление что это слово из того же ряда что и нанотехнологии например - это то о чем все говорят, но чего невозможно увидеть-обнаружить невооруженным взглядом.
"В данном контексте" - значит что вы имели ввиду, говоря
А что мешает использовать в нескольких микросервисах общую библиотеку?
Лично для меня "библиотека" это может быть или статическая библиотека как объект сборки программы. Или динамическая библиотека как объект, содержащий некоторый набор функций, используемых разными программами.
А на нашей платформе "библиотека" это вообще объект файловой системы (некий аналог "папки" в винде, к примеру).
У меня почему то складывается впечатление что это слово из того же ряда что и нанотехнологии например - это то о чем все говорят, но чего невозможно увидеть-обнаружить невооруженным взглядом.
У меня складывается впечатление, что рассуждая о микросервисах люди подразумевают их конкретную реализацию - контейнерную (когда каждый микросервис работает в отдельном контейнере). А все иные реализации - это уже не микросервисы, а "модульные монолиты".
Хотя, на самом деле, контейнеризация отнюдь не обязательное условие микросервисной архитектуры
контейнеризация отнюдь не обязательное условие микросервисной архитектуры
мне кажется что это описание:
философию Unix, согласно которой каждая программа должна «делать что-то одно, и делать это хорошо» и взаимодействовать с другими программами простыми средствами
это вообще не о чем! Потому что любой исполняемый файл (программа) и так, по определению, делает что-то одно, и взаимодействует с другими программами простыми средствами (командная строка, консольный ввод-вывод) , получается что мы просто теперь любой исполняемый файл называем модно-молодежно микросервисом, здорово конечно.
Вот и мне непонятно. Когда говорят о микросервисах - имеется ввиду концепция отдельных модулей, каждый из которых выполняет атомарную логическую функцию и взаимодействует с остальными, или же о конкретной "контейнерной" реализации когда каждый модуль работает в отдельном контейнере со своей копией БД и вот это все вот...
мешает то что она общая. вы её измените для своего сервиса, и вам нужно звонить лиду соседней команды или всех команд кто её ещё использует и просить их тестировать ваши изменения. общая библиотека это распределённый монолит - антипаттерн микросервисов
А как это, изменить общую бибиотеку под себя, я этого не понимаю! Как вы совмещаете слова "общая" и "измените для своего сервиса" в одном предложении? Плюсовую библиотеку STD тоже можно для своего сервиса изменить следуя вашей логике?
Если вам нужна уникальная функциональность не надо ее пытаться воткнуть в общую библиотеку, по моему это очевидно.
Вроде как получается в ответ на ваше (вполне актуальное кстати) замечание что просто надо уметь различать-разделять общее от локально -уникального, красивые умные слова:
распределённый
монолит
антипаттерн
микросервис
здесь в общем то не при чем.
платформенный код например. общая библиотека это не то что вы с нюгета скачали, а то что вы туда положили. и через какое-то время такие общие библиотеки приходится обновлять, хотябы потому что обновляются библиотеки которые ломают тесты (потому что Microsoft например обновила какие-то свои механизмы). и вам придётся согласовывать такие вещи с другими командами, и вот сервисы как раз позволяют этого не делать. ну тоесть у каждого свой код, общие только протоколы и DTO. ну кагбэ я с вами не то чтобы спорю а просто в моей практике сталкивался с такими проблемами как обновление платформенного кода, который на 50 сервисах работает, и это было геморно. но зато не надо было заморачиваться с многими другими вещами типа healthcheck, проверка целостности, удалённое управление сервисами итд... это такая тема которая к искусству ближе чем к науке на моей взгляд
Ох уж эти holly wars.... (доставая попкорн :)
Можно обсудить, что писать микросервисы нужно только тогда, когда подразумевается большая нагрузка, чтобы в один момент всему не упасть, если такое не предвидится, то зачем создавать сложности с обслуживанием этих микросервисов?
Критерий крайне однобокий. Собственно, жду когда наконец придёт осознание, что никаких "микросервисов" не существует. Есть программы и программные комплексы.
Ну вот у нас большая нагрузка. Но нет микросервисов. Но есть модульность. И "все" в один момент никак упасть не может.
То, что называется микросервисами, есть всего лишь частный случай реализации модульной архитектуры. И не единственный.
Упорные попытки противопоставить их какому-то абстрактному "монолиту" не более чем прием "сам придумал, сам опроверг, ай какой я молодец".
Холиварить - неотъемлемая часть профессии
К тому времени, как многие крупные компании только раскачались, уже созрели и готовы использовать микросервисы, уже "они не нужны". Ну камон! :)
У монолитных больших сервисов даже если грамотно спроектировать все объекты и методы, что уже будет непросто так как некоторые вещи станут известны гораздо позже... скорее всего начнутся проблемы с распределением памяти и других ресурсов. Поэтому если проект очень большой и имеет большое количество логически разделяемых сервисов то микросервисы пожалуй единственное решение позволяющее масштабировать проект без потери контроля за всей инфраструктурой. Имхо
А объясните мне, за счет чего в микросервисах можно "грамотно все объекты и методы спроектировать" а в монолите нельзя?
Тут какая-то логическая ошибка. Если кто-то не может грамотно разделить отвественность по функциональным единицам в тепличных условиях монолита, то в более сложных условиях с сетевым взаимодействием он и подавно не сможет.
В случае с микросервисами обычно нет необходимости проектировать все сервисы одновременно (я говорю сейчас о сотнях микросервисов). Попробуйте спроектировать монолит такого масштаба и предусмотреть все необходимые ресурсы... думаю это врядли получится. Я уже не говорю про то, на сколько будет дороже иметь один такой супер-пупер сервер с терабайтами рам и дискового пространства и насколько сложно его будет обслуживать.
Не понял. Т.е. очевидно, если у нас монолит, то чтобы получить систему нужно реализовать весь необходимый функционал. А в микросервисах есть какая-то магия которая позволяет получить систему реализовав не весь функционал?
Про терабайты памяти. Видимо, помимо магии микросервисов за бортом моего понимания остался негласный запрет разбивать конечную систему на разумное количество составных частей. Так, чтобы все не было в одном процессе, но и не создавать себе ад - необходимость оркестрации сотен составных частей. Т.е. должен быть или обязательно монолит, или микросервисы (если я правильно помню, микро - миллионная часть).
Какие я вижу плюсы микросервисов:
Малый размер и малое время старта. На кластере в 100-300 инстансов можно сделать так, чтобы падение любого инстанса пользователю заметно не было, и не ломало общую картину распределения нагрузки. С монолитом это намного сложнее реализовать.
Канареечный старт. Стартуем обнову одной фичи на фокус-группу, если что-то пошло не так - откатываем. Простой - ноль, пользователь ничего не заметил. Старт монолита же займет огромное время, скинет сессии, и так далее.
Разделение релизов от разных команд, больше обнов прода, быстрее циклы разработки.
Минусы:
Обеспечить связность. Это на самом деле ад уже тогда, когда у тебя 10-20 компонент. Нужен проворный техпис, который будет с немецкой точностью и пунктуальностью все отслеживать. Малейший косяк в документации, и всё, многочасовое веселье гарантировано.
Сериализация/десериализация данных. Вроде бы пустяковая мелочь, но нет - в микросервисах она дает приличный такой процент нагрузки, особенно когда ходят километровые портянки данных в одном запросе.
На кластере в 100-300 инстансов можно сделать так, чтобы падение любого инстанса пользователю заметно не было, и не ломало общую картину распределения нагрузки
Вот этот момент не совсем понятен.
Вы говорите о сервисах, которые все делают одно и тоже и их множественность нужна только для распределения нагрузки, или о сервисах, каждый из которых делает что-то свое (свою особенную функцию) и вызываются по требованию?
Старт монолита же займет огромное время, скинет сессии, и так далее
Ну неужели кто-то еще делает реальные одномодульные монолиты огромного размера? Что-то мне не верится в это... Сколько приходилось сталкиваться, даже в конце 90-х - начале 00-х, уже делали разбивку на модули, каждый из которых реализует что-то свое (и сам такое писал). А когда есть разбивка на модули, то
Разделение релизов от разных команд, больше обнов прода, быстрее циклы разработки.
уже совсем не проблема.
Вот этот момент не совсем понятен.
Когда падает большая дура-монолитина в популяции 20-50 штук, потому что пользователь залил какую-то разновидность zip-bomb (в том же ворде их можно тысячи сделать, причем часто не специально) - это авария, потому что на остальные пошла бошльшая нагрузка - пока оно поднимется за полчаса... Когда падает файловый загрузчик-наносервис которого 300 штук популяция, он перестартовывает через пару секунд - ни значимого перераспределения нагрузки, ни простоя нет. При этом тяжелые модули сидят в меньшем числе, но спроектированы так, что уронить их извне почти невозможно - все, что идет от пользователя, уже причесано микро- и наносервисами, если что - падают именно они, а не разом все приложение. Та же петрушка с конвертерами, разархиваторами или распознавалками - они выделены в атомарный наносервис, их много, и их падения по ООМ - нормальный рабочий процесс, не ломающий приложение.
Ну неужели кто-то еще делает реальные одномодульные монолиты огромного размера?
Да полно в ынтерпрайзе их. Особенно там, где айти - совсем непрофильная деятельность, и все висит на поддержке у дешевой галеры.
Честно для меня это звучит примерно как: "а давайте все функции вызывать не просто CallFunction(a,b,c) а через сеть. И оно как-то там волшебно будет эффективнее работать."
Малое время старта? Да вроде монолит тоже быстро (мгновенно ) стартует. Если мы про юникс процесс говорим который читает конфиг и начинает слушать сеть.
Старт монолита же займет огромное время, скинет сессии, и так далее.
о чем это вообще? Старт процесса займет сходное время, сессии в смысле TCP подключения? Да тоже не обязательно если монолит за каким-то своим фронтендом ( условно nginx ).
Разделение релизов от разных команд, больше обнов прода, быстрее циклы разработки.
компенсируется тем что ломание протокола одной командой останется незамеченным гораздо дольше. В монолите где вы просто вызываете функции заметить такое намного проще и быстрее. Да и реже происходит в принципе.
Я лично видел, как старт крупного страхового монолита занимал 40 минут... Жарник под гиг в вебсфере, оракл базы (архив, быстрая, медленная, ...), сотня интеграций, кеши в памяти под терабайт - вот где боль. И когда это нарезают на модули да микросервисы, начинается кайф - перезапускать сразу все нужно в исключительных случаях, а так взял, перезапустил нужное за минуту и доволен. Да, какие-то сервисы стартуют дольше минуты, но их и трогают реже - кеш словарей и кеши частых запросов те месяцами могут не обновлятся, когда как мидл обновляется ежедневно.
Дробление выгодно тоже до определенного предела - многие начинают увлекаться и мельчить, от чего приходит ад связности, когда непонятно, кто куда и зачем ходит.
Все верно. Крупная компания с 1000 разработчиков разбивает систему на 50 сервисов по количеству команд и крупных задач. А фирмочка с 20 девелоперами и тим лидами,которые хотят как в Netflix, создают 200 микросервисов, чтобы быть такими же крутыми, как они прочли в заметках на LinkedIn
Почему, когда начинают сравнивать микросервисную и монолоитную архитектуры не упоминают микроядерную архитектура, которая по сути является промежуточным звеном между монолитом и микросервисом, и сервис ориентированную архитектуру, которую мы может наблюдать от графических интерфейсов рабочих столов, игрушек, до облачных провайдеров (Amazon, GCloud, Azure)?
Просто посмотришь на некоторые проекты, которым ни монолит, но микросервисный подход не нужны, но народ мучается, потом это ещё в кубернетес пытается "затолкать".
Возможно, микросервисы вам не нужны