Comments 44
Я его внедрял на одном из проектов вместе с husky, зашло очень даже хорошо.
Да, lint-staged это прекрасная вещь, мы его тоже используем, и тоже с хаски на прекоммите — потому что линтинг всех корректных файлов монолита занимает около двух минут.
Про slowlint у меня, видимо, плохо получается пояснять. Попробую ещё раз здесь.
Предпосылки такие:
- У нас было большое количество файлов, которые не проходят линтинг. Почти все, на самом деле.
- Хотелось, чтобы линтинг был в CI, но при этом не мешал разработчикам.
- Хотелось, чтобы линтинг шёл постепенно. Прогонять
--fix
по всему репозиторию — чревато ошибками, грозит потерей верхнего слоя истории, мердж конфликтами, да и не поправит автомат и половины ошибок. - Хотелось, чтобы новые файлы обязательно соответствовали нашим стандартам.
Можно было бы вогнать всё в .eslintignore, но тут проблема — если это сделать, то любая IDE сразу применит его и перестанет использовать линтер — так что ничего лучше не станет. Хотелось таки мозолить глаза разработчикам. Поэтому slowlint делает очень простую вещь — он позволяет добавить дополнительный файл а-ля .eslignignore, который не воспринимается IDE, но используется при проверках. К сожалению, из коробки в eslint нет возможности использовать два игнор файла, иначе надстройка была бы не нужна.
В результате, файлы у нас делаятся на три категории:
1) Игонорируется в .eslintignore — те вещи, которые не надо трогать никогда — например, легаси библиотеки. CI, IDE и линтер их игнорирует. В целом, у нас уже нет такого кода в репозитории.
2) Игнорируется в slowlint — те вещи, которые надо исправить. IDE не воспринимает этот игнор, ошибки мозолят глаза разработчикам, код постепенно переписывается и удаляется из игнор листа. А ещё этот список можно автоматически перегенерировать при помощи slowlint, если внезапно куча некорректного кода исправилась или куда-то уехала.
3) Нигде не игнорируются.
Звучит несколько сумбурно, но отлично у нас отработало.
Если же у вас новый проект, или небольшая кодовая база, которую можно быстро поправить — вам эта утилита не нужна. В микросервисах мы её нигде не используем.
Во-вторых, всю жизнь он провёл под хайлоадом (сейчас это 23 млн запросов в час).
23М / 60 мин = 383к рпс (ну пусть 400к будет)
400к рпс на одну инстанцию (раз это монолит) как-то жирно. Если монолит, то это 1 инстанс? или это все же монорепо?
Тем более, в случае Node.JS — будь это так — мы бы на одном потоке не пережили бы и открытие сервиса.
Инстансов у нас было большое количество, ещё и с распределением по ролям, окружениям, балансировкой — но это уже отдельная большая история.
Циферки RPS на самом деле мало чего значат без понимания, что именно за запросы идут, и с чем можно их сравнивать, поэтому я их не очень люблю приводить — слишком многие начинают сравнивать тёплое и мягкое.
Интересно, каким образом отказ от монолита устраняет это зло?
Так что ни искать все использования компонента, ни выкатывать изменения вместе не требуется — если сервису не нужен расширенный набор данных, то там и десериализацию обновлять не надо.
Все-таки, складывается ощущение, что с переходом на микросервисы вы пересмотрели принципы проектирования системы, что и дает существенную часть бонуса. Понятно, это само по себе можно отнести к плюсам микросервисной архитектуры.
"1. Боль в монолите", кажется, имеет мало отношения к монолитности.
и если была возможность использовать его криво — то она не была упущена.
Это проблема культуры разработки: "использовать криво" — это про людей, а не про монолиты.
Любое действие может вызвать абсолютно непредсказуемые эффекты, и не все из них отслеживаются
Это проблема дизайна: как сделать так, чтобы глядя на код было очевидно какие эффекты он вызывает. Микросервисы эту проблему вообще никак не адресуют.
Хочешь Express? Линтер? Другой фреймворк для тестов или моков? Обновить валидатор или хотя бы lodash? Обновить Node.js? Извини. Для этого придётся править тысячи строк кода.
В команде кто-то пишет код, который работает только на одной версии lodash? А виноват снова монолит? :-) Понятно, что переходы на новые мажорные версии могут вызывать проблемы, но эти проблемы точно так же возникают и у микросервисов.
Что приводит к тому, что в релизе может быть по 60 задач и больше. такое количество вызывает мердж конфликты, внезапные синергетические эффекты, полную загруженность QA на разборе логов, и прочие печали.
Насколько я понимаю, процесс продиктован непредсказуемостью ("Любое действие может вызвать абсолютно непредсказуемые эффекты"). Т.к. микросервисы эту проблему даже не пытаются решить, не понятно в чём здесь виноват монолит.
Сложный контекстный поиск (не забывайте, у нас нет статической типизации)
Это проблема JS и к монолитам отношения не имеет. Тут даже наоборот — будь у вас статическая типизация, монолит делал бы любую работу с кодом проще, чем микросервисы.
Очень часто возникают задачи с непонятной сферой ответственности — например, в смежных библиотеках.
С технической точки зрения у микросервисов ещё хуже: если 80% задачи сводится к правкам в одном сервисе, возникает искушение остальные 20% засунуть в тот же сервис, даже если этому коду там совсем не место. Искушение, конечно, вызвано тем, что зарелизить один сервис "проще", чем два.
С точки зрения рабочего процесса конечно же самый замечательный вариант когда работа ведётся "над фичами продукта", а не "над кусочками кода". Монолит отражает фичи более органично в том плане, что "весь код — в одном месте". С микросервисами фичи превращаются в "вот этот код, вон тот код, дождаться вон того релиза, и вот потом-то уже сможем зарелизить нашу фичу".
Потекла память? Выросло потребление CPU? Захотелось построить флейм графы? Извини. В монолите одновременно происходит столько всего, что локализовать какую-то проблему становится безумно сложно. Например, понять, какая из 60 задач при выкатке в продакшн вызывает повышенное потребление ресурсов (хотя локально, на тестовых и стейджинг средах это не воспроизводится) — почти нереально.
С микросервисами ещё интереснее — если сервис X внезапно стал потреблять в 10 раз больше ресурсов, из этого совершенно не следует, что проблема — в сервисе X. Часто бывает, что проблема в сервисе Z, который вызывает сервис Y, который в цикле дёргает X по 20 раз на каждую транзакцию :-)
"использовать криво" — это про людей, а не про монолиты.
Повторюсь. Проблема — не в факте кривизны использования, а в количестве переиспользований, которые надо проверить за раз.
Это проблема дизайна: как сделать так, чтобы глядя на код было очевидно какие эффекты он вызывает. Микросервисы эту проблему вообще никак не адресуют.
Повторюсь. Меньше кода — проще понять.
Понятно, что переходы на новые мажорные версии могут вызывать проблемы, но эти проблемы точно так же возникают и у микросервисов.
Повторюсь. Перейти на мажорную версию, поправив 10 строчек — легко. Перейти на мажорную версию, поправив 1000 строчек — кошмар.
Т.к. микросервисы эту проблему даже не пытаются решить, не понятно в чём здесь виноват монолит.
С микросервисами нам стало проще локализовать проблему в конкретном сервисе.
Это проблема JS и к монолитам отношения не имеет. Тут даже наоборот — будь у вас статическая типизация, монолит делал бы любую работу с кодом проще, чем микросервисы.
Это проблема JS+монолита. Я и имел в виду, что при статической типизации было бы порядком проще.
Искушение, конечно, вызвано тем, что зарелизить один сервис "проще", чем два.
В нашем флоу, разработчик делает задачи, и ничего не знает о релизах — так что ему всё равно, сделать правку в одном сервисе, или в двух.
С микросервисами фичи превращаются в "вот этот код, вон тот код, дождаться вон того релиза, и вот потом-то уже сможем зарелизить нашу фичу".
Да нет, просто релизим, но не включаем. Проверить её работоспособность можно и без зависимых микросервисов.
Часто бывает, что проблема в сервисе Z, который вызывает сервис Y, который в цикле дёргает X по 20 раз на каждую транзакцию :-)
Ага. На это есть дашборды, на которых девопс должны увидеть возникшую аномалию в коммуникациях между микросервисами.
Проблема — не в факте кривизны использования, а в количестве переиспользований, которые надо проверить за раз.
Не получается понять что всё-таки вы имеете в виду под этой необходимостью "проверять много переиспользований". Это какая-то ручная работа?
Меньше кода — проще понять.
Так ведь его не меньше, его как минимум столько же. Только теперь вместо "Ctrl+click, Ctrl+click, Ctrl+click, о, всё понятно" это превращается в: "а, тут мы дёргаем другой сервис, окей, пошёл смотреть его код".
Перейти на мажорную версию, поправив 10 строчек — легко. Перейти на мажорную версию, поправив 1000 строчек — кошмар.
Я понял, вы имеете в виду: "с микросервисами моя задача, как разработчика, вероятно, будет ограничина одним микросервисом".
В нашем флоу, разработчик делает задачи, и ничего не знает о релизах — так что ему всё равно, сделать правку в одном сервисе, или в двух.
Вы в статье написали: "часто возникают задачи с непонятной сферой ответственности", а теперь "ему всё равно, сделать правку в одном сервисе, или в двух". Как монолитность-микросервисность влияет на владение кодом?
На это есть дашборды, на которых девопс должны увидеть возникшую аномалию в коммуникациях между микросервисами.
Вы удивительным образом одновременно делаете громкие заявления о мониторинге и тут же изолируетесь от него, указывая, что есть некие отдельные девопсы и это их головная боль :-) Приходят все метрики из разных частей одного монолитного сервиса или из совершенно разных сервисов — разницы нет. При монолитном подходе точно так же можно навешать измерялок на любые интересные части кода и мониторить их не "в среднем", а по-отдельности. Это же не какая-то магия, которая у микросервисов есть, а у монолитов нет.
Не получается понять что всё-таки вы имеете в виду под этой необходимостью "проверять много переиспользований". Это какая-то ручная работа?
Ага. Например, замена кода при breaking changes, или проверка использования каких-то deprecated методов.
Так ведь его не меньше, его как минимум столько же. Только теперь вместо "Ctrl+click, Ctrl+click, Ctrl+click, о, всё понятно" это превращается в: "а, тут мы дёргаем другой сервис, окей, пошёл смотреть его код".
Крайне редко приходится смотреть что-то ещё, обычно скоуп задачи ограничивается одним сервисом. И даже если двумя, то это всё равно меньше кода, чем совокупность двух десятков сервисов в монолите.
Я понял, вы имеете в виду: "с микросервисами моя задача, как разработчика, вероятно, будет ограничина одним микросервисом".
Нет, я имею в виду, что много маленьких итеративные правки проще одной огроменной.
Как монолитность-микросервисность влияет на владение кодом?
Снова повторюсь. Обычно известно, какая команда отвечает за тот или иной микросервис целиком.
При монолитном подходе точно так же можно навешать измерялок на любые интересные части кода и мониторить их не "в среднем", а по-отдельности. Это же не какая-то магия, которая у микросервисов есть, а у монолитов нет.
Разделить использование RAM или CPU по исполняемым функциям на продакшне — не такая простая задача. Она тоже решаема, но распределить по микросервисам проще.
«Это проблема JS и к монолитам отношения не имеет. Тут даже наоборот — будь у вас статическая типизация, монолит делал бы любую работу с кодом проще, чем микросервисы.»
Ну да, в проекте с несколькими десятками тысяч файлов ваша IDE будет просто летать. (Нет к вам будут бегать разработчики и просить более мощные рабочии станции)
Это время выполнения всех или только тех, с которыми связаны изменения в коммите?
Что было самым результативным при правке тестов?
Так что это время прогона всех юнитов. Наиболее результативным оказался поиск самых долгих тестов и их профилирование. Например, отрыли древний тест, в котором разработчик решил проверить, что несколько тысяч случайно сгенерированных величин будут действительно случайны… И мы проверяли это несколько лет!
Не, ну тут Вы просто себя не любите. Понятно что 9 лет назад typescript еще не было, но уже лет пять как им уверенно можно пользоваться.
Тоже шел подобными путями пару раз, лично у нас было еще +30 всяких разных интересных проблем. Понимаю что они просто не вместились в презентацию.
Хочу пару моментов уточнить
1. Удалось ли при такой поэтапной миграции полностью отказаться отказаться от старого кода в монолите? И была ли такая цель вообще?
2. В целом по моему опыту сложность продукта, доменной области в монолите приводила к вышеуказанным проблемам, и при миграции на микросервисы вы эту сложность перекладываете на плечи девопсов. На сколько больше у вас стало девопсов в итоге? Или как оценить примерно сколько людей девопсов понадобится например через год-два после миграции 30% функционала на микросервисы? Мс и монолит у вас on-premise?
Да, проблем было гораздо больше, но я был ограничен и временем доклада и размером статьи. Огромное мало кто прочитает. Судя по комментариям, и эту длину многие читали по диагонали.
- Удалось ли при такой поэтапной миграции полностью отказаться отказаться от старого кода в монолите? И была ли такая цель вообще?
Да нет, цели такой не было. Код может быть не оптимален, но в нём заложено много разной нетривиальной логики, так что надёжнее идти путём итеративных улучшений. Основная задача, которую мы решали — это взаимодействие и релизы большого количества команд.
На сколько больше у вас стало девопсов в итоге?
Ну, в самом начале у нас было ноль девопсов, так что в любом случае их стало бесконечно больше. Сейчас команда девопс насчитывает около пяти человек. Сложности и потребность в увеличении команды возникли не столько из-за микросервисов, сколько из-за сознательного применения различных технологий и подходов. Если бы мы остались только на ноде на микросервисах стандартной конфигурации — думаю, пары человек бы хватало.
Мс и монолит у вас on-premise?
Да, конечно. Свои облака, мощности, поднимаемые по запросу и в ответ на нагрузку, и прочие радости.
1. Если вас на беке пара человек — пишите монолит и не гонитесь за модой в девопсе. Уже на десятке микросервисов вы утонете.
2. Если вас тридцать человек — никаких монолитов, вы в нём запутаетесь.
Т.е. проблема по сути одна и та же, только поворачивается разными углами — проблема взаимодействия. Когда один разраб пишет несколько микросервисов — у него всё время будет уходить на их взаимодействие между собой, а не на бизнес-логику. Когда толпа разрабов пишет один код — у них будут проблемы во взаимодействии друг с другом.
Да, как-то так. Главное — вовремя поймать момент, когда первое состояние переходит во второе, и осилить перейти. Но до него ещё нужно дожить, что выходит не так часто, как хотелось бы.
TPC & UDP, Redis, NATS, kafka, итд.
Везде где есть потокавая передача данных можно использовать как транспорт. Главное формат общения был запрос ответа был под стандартом.
Извиняюсь не вам отправил, хотел ниже человеку отправить noize
С телефона не так удобно использовать комментарии.....
Расскажите пожалуйста про шину обмена данными между микоосервисами и что используется в качестве протокола для обмена сообщениями
Чуть выше писал — http, redis, rabbit. Обмен по большей части в JSON или ProtoBuf. Наверняка что-то ещё есть, о чём я не знаю. Какой-то единой шины нет — может быть, это принесло бы нам счастье, но навскидку не могу сказать проблемы, которую она бы решила. Но тут как — возможно, я просто не осознаю, что она есть.
и таки да, с большенством тезисов согласен… и дублирование кода, и мусор, и чего только там нет… разные условия решения задачи, сжатые сроки и т.п., вплоть до того, что в некоторых частях кода захардкожены креды к БД…
даже не знаю чья это проблема… бизнеса или разработчика, палка о двух концах… Задач постоянно много, фьючеров постоянно много и некоторые нужны на вчера и т.п., потому что фьючер продается будто бы он уже есть, чтобы не городить то что не будет восстребованным…
до сих пор борюсь в дев.подходом компании, и если в течении 6-7 лет разрабатывал все в одиночку, огромный монолит для 3х ветвлений компании (селл, телемаркетинг, клиенты), то с созданием полноценного офиса разработчиков, привлечением новых кадров и т.п. — работы ни у кого меньше не стало, видимо дело в манере ведения бизнеса компании относительно разработки, и конечно — прибыль компании и рост доходности возвращаються болью разработчиков…
Но вообще, микросервисы, в результате, дали возможность группе разработчиков или одному концентрироваться лишь на конкретной части, в результате: где-то нода, где-то го, гдето даже си… а обмен — http или голый tcp, хотя и grpc был на основе protobuf — но стал выстрелом в ногу, даже в две, а потом и в голову…
крч, сложно это все, и таки да — идеального решения нет,
есть потребность бизнеса и конкуренты не будут ждать идеальный код перфекциониста. Сделали чтобы работало — и поскакали далее, а там разберемся)
есть потребность бизнеса и конкуренты не будут ждать идеальный код перфекциониста.
Да, такое есть. Видел компании, в которых писали идеальный код — обычно они так или иначе прогорали. Очень важен баланс между разработкой и интересами бизнеса — с одной стороны чтобы развиваться и получать деньги, с другой — чтобы не утонуть в костылях и быстрокоде.
но стал выстрелом в ногу, даже в две, а потом и в голову
Да, ничего не даётся бесплатно. И при переходе на новые технологии и архитектуру ты обязательно сперва выстрелишь себе в ногу — вообще без вариантов. Нужно просто это пережить.
Я радуюсь, когда делаю на микросервисах что-то.
Использую активно molecular, так как посчитал что базовые фишки я буду делать долго и не факт, что сделаю лучше с первого раза. Из плюсов
Нормальное логирование.
Настройка метрик на методы.
Отправка метрик во все возможные сервисы.
Единый формат общения между сервисами.
Масштабируемость сервисов из коробки.
Тестировать код, мне стало проще. Микроскопы могут сами подыматся в случаи падения.
Наверное все не перечислить из плюсов.
Минусы были, они касались только некоторых мексинов, которые благополучно заменены.
Текущий монолит работает 700-1000 запросов сек.
4.1 Можно делать гетерогенную инфраструктуру
Теперь мы можем писать код на cтеке, который оптимален для решения конкретной задачи. И рационально использовать любых хороших разработчиков, которые к нам попали
К сожалению в подавляющем большинстве случаев это скорее минус нежели плюс. Не надо забывать что код надо больше поддерживать в будущем и адаптировать, нежели написать один раз и забыть о нем. Не надо забывать что разработчики не только приходят в проект но и уходят из проекта. Очень печально видеть проекты в которых в команде из 5 человек ипользуют 3-4 стека технологий на микросервисах, потому что каждый новый пришедший человек считал что стек X отстой, стек Y крут, стек Y отстой, стек Z крут и т.д.
А потом когда человек уходит из проекта (по самым разным причинам) иногда всю работу приходиться полностью переписывать с нуля, так как или разраба не могут найти на этот стек или там все очень плохо написано на самом деле: у кого то качество покрытия логов реализовано плохо, у кого то вообще реализовано с SQL инъекциями. А код ревью не сильно спасет так как надо разбираться в коде в другом стеке.
Не надо разводить зоопарк просто потому что разработчик хороший, это обходиться потом очень и очень дорого.
Как-то писал 3,5 года похожий проект. .net, 200 метров кода, одних адаптерам к разным hotel-booking api — под 100 штук, таблиц в нескольких БД под 1000.
Там был довольно мудро структурирован код — монорепа, но побитая по проектам, которые могли и как библиотеки подключаться, так и как отдельные сервисы с http api. Т.е. а были границы, через которые можно кидать только сериализуемые dto-шки. Хочешь как монолит запусти, хочешь каждый сервис на своей ноде.
Подход очень неплохо работал. Весь код открывать в ide, билдить, и запускать — не обязательно. Как разбить по нодам при деплое — можно разные варианты пробовать. Можно было логировать весть вход-выход любого модуля, и это было просто бомбически удобно для дебага.
При этом все плюсы монорепы и монолита были на месте.
.net и кучу библиотек вокруг — без проблем обновляли: с. net 1.1 дошли до 4.5. Т.е. начали даже без дженериков, а под конец уже фигачили лямбды. И сбилдить все 200 метров кода — тогда. (10 лет назад) было быстрее, чем сейчас 20 метров тайпскрипта с вебпаком. Так что проблема обновить lodash и сбилдить монорепу — это чисто нодовские приколы.
9 лет в монолите на Node.JS