Transcription
Микросервисы сегодня везде. В каждой вакансии, на каждом проекте. Умение их разрабатывать — это навык, совершенно необходимый для любого современного программиста. И в этом ролике, с великолепными анимациями, мы поговорим о них крайне подробно и супер простыми словами. И я гарантирую, что к концу видео вы будете, ну, просто прекрасно понимать, что такое микросервисы и зачем вообще.
Привет, меня Влад зовут. Я разработчик с семью годами опыта на бэкенде в одной из лучших компаний в мире и живу в Амстердаме. На своём канале рассказываю о том, как тебе стать супер мощным программистом в кратчайшие сроки.
В этом ролике мы с вами поговорим обо всём, что касается микросервисов. Мы начнём с самого начала: откуда они вообще взялись, то есть что такое монолит, какие у монолита были проблемы, потом как появились микросервисы, какие преимущества этих микросервисов есть. В то же время, какие у микросервисов есть проблемы, потому что это не бесплатное решение, и, очевидно, оно требует от вас решения каких-то дополнительных задач. Ну и также мы обсудим самый важный, насущный вопрос: в своих проектах, что вам использовать — монолит или микросервисную архитектуру? Всё это, всё повествование будет сопровождаться для максимального вашего понимания материала. Погнали!
Ну что ж, давайте сначала разберёмся, какую проблему вообще решают микросервисы, чтобы понимать, зачем они вообще нужны, откуда взялись. И для этого нам нужно будет начать с понятия монолита. Давайте разбираться.
В общем-то, как у нас выглядит любое современное веб-приложение? У любого современного веб-приложения есть какие-то пользователи, которые с этим приложением будут взаимодействовать. И в современном мире мы, в общем-то, имеем два способа взаимодействия с веб-приложением. Это либо какой-то веб-фронтенд, то есть буквально то, что вы видите в своём браузере: всякие кнопочки, формочки, поля ввода и так далее. Это отдельное приложение, как правило, написанное на языке JavaScript с использованием какого-нибудь фреймворка React.
И второй способ взаимодействия в современном мире с любым веб-приложением — это мобильный фронтенд. Это те же самые формочки, кнопочки и поля ввода и так далее, но только на вашем смартфоне. Это всё ещё тоже фронтенд. Его разрабатывают для iOS на языке Swift, а для Android на языке Kotlin, например, или на Java. И вот эти два фронтенда, по сути, тоже программы, просто написанные на определённых языках, которые пользователь запускает на своём устройстве, либо на смартфоне, либо на своём компьютере. Вот он запускает браузер, видит этот фронтенд, то есть в браузере он запускается.
Эти две программы взаимодействуют с так называемым бэкендом. Это, в общем-то, основная программа, которая выполняет всю логику того приложения, о котором мы говорим. То есть там сосредоточены все основные функции и всё основное взаимодействие, все данные хранятся именно там и так далее, и так далее. То есть, когда вы жмёте какую-то кнопку на сайте на фронтенде, на вот этом, то данные по интернету отправляются на бэкенд, в другую программу, которая написана вообще на другом языке, например, на Java, и запущена на отдельном компьютере, который называется сервером. Компьютер этот принадлежит компании, которая разрабатывает это приложение. Это называется бэкенд, потому что эта программа находится позади фронтенда, как бы, да? Пользователь с ней не взаимодействует явно, всё происходит по интернету, невидимо для пользователя. Ему кажется, что он нажал кнопку, и в этом же браузере у него что-то появилось. Так вот, такая программа называется бэкендом.
Но, кроме того, там, скорее всего, ещё существует какая-то база данных, тоже отдельная программа, которая умеет сохранять данные, что мы передаём по интернету, на жёсткий диск какого-то компьютера, на котором, в общем-то, база данных программа буквально запущена. Вот так вот организуется взаимодействие в любом современном приложении на высоком уровне.
Но раз уж вот в этой программе, которая запущена как бы на бэкенде, сосредоточена вся основная логика приложения, то можно себе представить, что это довольно массивное приложение, в нём очень-очень много кода и разнообразного функционала. Если вы посмотрите на современные приложения, то увидите, что в них очень много разных функций, каждая кнопка делает определённое действие и так далее, и так далее. Соответственно, исторически так сложилось, что когда люди писали бэкенд для своих приложений, то есть вот эти основные программы, они писали их на, например, языке Java и на фреймворке Spring для того, чтобы организовать вот это веб-взаимодействие по интернету. И внутри всего этого приложения, и внутри единой программы, писали весь код для всех разных фич.
Например, если мы делаем приложение социальную сеть, то в этой одной программе, написанной на Java и запускаемой как бы на бэкенде, у нас будет и логика создания постов в нашей социальной сети, и логика отправки нотификаций пользователям о том, что им там оставили комментарии под этим постом, и логика самих комментариев, и логика создания ленты новостей для пользователей, и какая-то система рекомендаций, чтобы предлагать пользователям определённый контент, который их интересует, да? И вот эти наши веб-фронтенд и мобильный фронтенд, они по интернету всё также взаимодействуют с этим приложением, внутри которого сосредоточен весь этот функционал. Все функции, которые есть, они лежат там.
Вот эти сами функции, они уже могут взаимодействовать с какими-то внешними компонентами. Например, логика рекомендации может взаимодействовать с какой-то там Machine Learning моделью для обеспечения результатов этих рекомендаций. А логика нотификации, например, может использовать там, не знаю, сервис отправки электронных писем для реализации, в общем-то, этих нотификаций.
И вот эта программа, запускаемая на бэкенде и написанная таким образом, что все её фичи находятся как бы в рамках одного программного проекта, буквально — это единая кодовая база. Вы можете либо всё приложение запустить, либо вообще его не запускать, только вместе со всеми частями. Вот такая архитектура, где всё вместе находится в одном приложении, называется монолитом. Монолит — значит неделимый. То есть оно всё вместе, единым большим куском.
На этой картинке монолит выглядит довольно простым. Он состоит из четырёх больших фич, которые как бы объединены в одно приложение. К сожалению, же современные приложения они гораздо больше, в них намного больше функционала, и монолит становится просто огромным. Это огромная кодовая база на сотни тысяч строк кода. И чем больше становится такой монолит, тем больше у него появляется проблем. Давайте поговорим о том, какие проблемы у монолитов есть.
Первая, самая яркая проблема монолитов, называется жёсткое связывание. Дело в том, что вот эти фичи, отдельные, которые как бы отдельные логика, никак не интересующаяся друг другом по факту, да, она находится в единой кодовой базе, в едином проекте. И получается, что разные компоненты, которые отвечают за разные части работы вашей системы, очень жёстко связаны между собой. Они буквально вызывают методы друг друга, они буквально используют часто компоненты, которые относятся к другому модулю вашей программы, в своей логике. В итоге между ними появляются такие сцепки, вот эти связи очень-очень тесные, потому что они взаимодействуют с помощью друг друга. Это буквально код, который вызывает другой код в едином проекте, прямо функцию дёргает какую-то.
И получается, что если я как программист хочу поменять какой-то функционал моей системы, я не знаю, вношу какие-то изменения, меняю какую-то фичу, я, допустим, хочу изменить фичу ленты новостей, и я делаю какое-то изменение в её коде. Но из-за того, что она в рамках монолита очень сильно связана с другими компонентами, то есть они, эти компоненты, могут использовать её код, она может использовать код других компонентов, получается, очень часто, что изменения эти затруднены. Меняя лишь одну часть моего приложения, из-за этих жёстких связей я зачастую вынужден проходиться ещё по ряду компонентов и вносить изменения в них, которые логически как бы не очень сильно относятся именно к ленте новостей. Возможно, они предоставляют для неё данные, но с точки зрения логики не кажется, что между ними существует какая-то сильная зависимость. Тем не менее, из-за того, что они буквально используют друг друга, очень часто я должен менять целую группу компонентов, хотя на самом деле изначально хотел изменить лишь один. И в итоге, буквально, процесс разработки мой сильно удлиняется. Да, я просто должен сделать больше работы, я должен изменить больше вещей.
Быстрая пауза, чтобы сказать спасибо за то, что смотрите это видео. Будет здорово, если поставите лайк и подпишитесь на канал. Это правда помогает. Более того, я не просто должен изменить множество вещей, я ещё и должен понимать, как они работают. Монолит — это огромный проект, в котором куча разного функционала, который делает абсолютно разные вещи. И меняя один компонент, я меняю другие. Значит, я должен понимать не просто, как мой изменяемый компонент работает, но и как работают все остальные компоненты, которые я так уж вышло, тоже должен изменять вместе со своим. В итоге я, как разработчик, должен глубоко разбираться во всём этом огромном проекте, что очень сложно. Он очень массивный, громадный просто проект, там куча кода, куча разнообразных решений принято, и я должен всё это великолепно понимать, чтобы делая изменения во всех своих компонентах, в общем-то, не накосячить.
Это, в общем-то, проблема ответственности за разные компоненты. Я, как разработчик, должен быть ответственным по факту за всё, что есть в этом монолите. И чем больше этот монолит становится со временем, буквально в него добавляются какие-то новые фичи, люди дописывают код, тем больше становится моя ответственность, тем больше про него я должен знать. Хотя в него код не всегда пишу я, у меня может быть команда из 100 человек, которые тоже туда что-то пишут, но я должен быть в курсе того, что они, в общем-то, делают. И самое, в общем-то, паршивое, что внося вот эту группу изменений по разным компонентам, которая вообще по факту цепная реакция, последовавшая за одним лишь изменением, которое я изначально хотел внести в ленту новостей, я, мало того, что изменяю эту логику, я ещё и должен убедиться, что все мои изменения работают правильно. Потому что я разработчик, я человек, я могу ошибаться. Если даже свои исходные изменения я сделал абсолютно правильно и проверил это, то существует ещё целая группа изменений, которые всё ещё могут содержать какие-то ошибки. Если вы меняете код какой-то работающий, то это всегда пространство для ошибок, потому что любой человек может ошибку допустить, любое изменение всегда нужно очень внимательно тестировать.
Но проблема в том, что связи между компонентами в монолите настолько тесные, что вы иногда можете даже не подозревать, что какое-то из ваших изменений затрагивает другие компоненты. Там это изменение выглядит неявным, его трудно обнаружить. И в итоге вы, например, проверяете ряд компонентов на правильное функционирование и, допустим, убеждаетесь, что они работают правильно. Но так вышло, что вы либо забыли, либо просто не заметили, что это нужно проверить, либо проверили некачественно какое-то ещё одно изменение в каком-то там вообще третьем компоненте, которое так вышло, было связано со всеми остальными, и там вот с теми изменениями, что у вас есть, вдруг возник новый баг. Да, именно в тех изменениях, которые вы внесли. И получается, что мы меняли вообще какую-то одну часть приложения, а баг вылез вообще в какой-то другой. И это очень сложно проследить, потому что у вас огромный код, его найти ну очень тяжело.
В результате получается, что вот в такой ситуации я менял логику ленты новостей и вообще не подозревал даже, как человек, что это может затронуть какие-то другие вещи, а у меня из-за бага отвалилось аж логика отправки нотификации, предположим. И это очень сложно найти. В итоге, когда люди разрабатывают такие приложения, они вынуждены тратить огромное количество времени на перепроверку постоянной работы всех компонентов, потому что они настолько жёстко связаны друг с другом, настолько сильна их зависимость друг от друга, что любые изменения в какой-то части вдруг вызывают подвижность других частей. И, кроме того, это чревато какими-то ошибками и отказом этих других частей, потому что они просто изменяются. В результате у вас просто качество приложения, которое вы разрабатываете, становится ниже. Вы постоянно делаете изменения, вам кажется, что ваш компонент работает правильно, но в итоге потом пользователи вам говорят: "У нас отвалилось вообще какая-то третья фигня". Вы туда смотрите, чешите репу и не понимаете: "А почему это отвалилось? Я же вроде здесь ничего не делал, а оно не работает". А оказывается, там сложная зависимость между разными частями вашей системы, которая и привела к падению, например, логики нотификации, хотя вы вообще её не трогали.
Впереди, разумеется, ещё много чего интересного. Но даже то, о чём мы уже успели поговорить, постоянно спрашивают на собеседованиях по микросервисам в лучшие компании в СНГ. В общем-то, поэтому я подготовил для вас совершенно бесплатную шпаргалку по самым популярным вопросам на собеседованиях по микросервисной архитектуре. Забрать её можно по ссылке в описании на нашем замечательном сайте, который называется "Библиотека Java Junior". Там, кроме этой шпаргалки по микросервисной архитектуре и вопросам со всеми ответами, в общем-то, вы можете найти ещё огромное количество материалов для подготовки к собеседованию. Там и по Docker, и по Go, и по Spring, чего там только нет. И эта библиотека ещё и постоянно будет пополняться. Поэтому переходите, совершенно бесплатно забирайте все материалы и готовьтесь к интервью в самой лучшей компании в мире. Погнали!
Ещё одна огромная проблема монолитов заключается в их деплое, или развёртывании. Что такое вообще деплой? Вы вот своё приложение имели в какой-то версии, скажем, версия один, да? И внесли вот в него какие-то изменения. Предположим, вы их протестировали, всё работает правильно и просто великолепно. И какой-нибудь ещё один программист, который в вашей команде работает, он тоже пришёл и внёс какие-то изменения вообще в другой компонент. Он, в общем-то, делал свою обособленную от вас работу, просто над тем же самым приложением. И вот вам вот эту новую версию, которая у вас получилась после изменений, нужно, в общем-то, доставить пользователям, чтобы они теперь применяли все эти фичи, которые вы туда добавили, да? Для этого вот берётся ваш код, он с помощью какого-нибудь инструмента собирается в единый исполняемый файл, который буквально можно запустить на каком-то компьютере. Например, для Java это делает инструмент, который называется Gradle. Он превращает ваш Java-код в файл с расширением .jar. Это то же самое, что у какой-нибудь компьютерной игры file.exe. Его можно просто взять и запустить. Точно так же и с вашим приложением из файла .jar.
После запуска этого Gradle, который ваш код собирает в этот файл, вы этот файл получаете, и вам нужно пойти и посмотреть на ваши сервера. Прямо сейчас у вас запущен вот один сервер, на котором крутится старая версия вашего приложения. Туда буквально загружен на этот сервер, внутрь положен, прямо в его файловую систему, файл старой версии вашего приложения. Это тот .jar файл, который собрали из кода предыдущей версии и положили на этот сервер. А вам теперь нужно развернуть новую версию вашего приложения. Ну, самый простой вариант — это поставить ещё один новый сервер, загрузить на этот новый сервер ваш новый файл, который вы только что собрали, версия 1.1, да, обновлённый .jar файл, запустить этот .jar файл на этом сервере, чтобы буквально запустить его. То есть на компьютере у вас там поднимается ваше обновлённое приложение, оно включается, и вы запросы пользователей, которые до этого шли на сервер со старым приложением, перенаправляете с помощью изменений конфигурации на новый сервер, буквально отправляете их на другой IP-адрес, и они теперь взаимодействуют с вашим новым приложением. Вот этот процесс доставки и развёртывания новой версии приложения на новом сервере для ваших пользователей, чтобы они могли с ним взаимодействовать, и называется процессом деплоя, то есть развёртывания вашего приложения, развёртывания новой версии.
Проблема монолитов здесь заключается в том, что вот в эту новую версию, которую вы собрали, вносили ведь изменения не только вы, не только вы как разработчик туда добавляли новые фичи, но и какой-то другой программист тоже что-то поменял вообще в отдельной задаче. Что если он туда принёс какой-то баг? Что если в его изменениях есть баг? Этот баг никак не затрагивает ваши изменения, но в этой новой версии, которую вы собрали со всеми изменениями, есть проблема. И, конечно, когда эта версия оказывается на серверах пользователей, они эту проблему замечают, начинают кричать, злиться, что вот, ничего не работает, у нас система рекомендаций сломалась. И в такой ситуации, очевидно, первое действие у разработчиков — это взять новую версию и полностью её откатить до предыдущей, которая работала стабильно. Но что это значит для вас? Что вы откатываете не только все изменения, которые вы внесли, но и все изменения, которые вы внесли, но которые работают. Все и ваши изменения тоже, хотя они великолепно функционируют, но из-за того, что это всё вместе, из-за того, что это один единый монолит, собранный в один файл, вы вынуждены деплоить все изменения сразу целиком. И если одно из них ломается, вы вынуждены всё откатывать. Таким образом, вы у пользователей при откате забираете не только неработающий функционал, но и новый работающий функционал. Они могли уже начать его использовать, а тут бац — и исчез. Ну, типа, не очень хорошо получается.
Ну и самая противная, наверное, проблема монолитов — это проблема масштабирования. Вот посмотрите на нашу архитектуру. У нас есть этот бэкенд огромный, который мы запустили на каком-то сервере. Сервер — это банально очень мощный компьютер, способный вот вытащить приложение, которое на нём работает, к которому люди обращаются по интернету. Это реально просто железная машина. Но железные машины они стоят денег каких-то. Мы буквально заплатили за какой-то сервер, там купили его или взяли в аренду, неважно, но он дорогой. Мы его поставили и запустили на нём приложение. Предположим, этот сервер стоит 3.000 долларов, допустим.
Итак, вышло, например, что сегодня в нашем приложении какой-то из знаменитых пользователей наших запостил какое-то там видео, и оно, например, попало в ленты его огромного количества подписчиков. Скажем, у него 100 млн подписчиков, какой-нибудь, не знаю, Лионель Месси это запостил. Этот пост собрал жёсткий ажиотаж, все начинают лайкать, комментировать. И нагрузка на логику нашей ленты новостей в нашем приложении сильно возрастает. Там выделяется огромное количество ресурсов для того, чтобы всю эту нагрузку обработать. Эти ресурсы всегда ограничены на этом компьютере. Нельзя иметь больше оперативки, чем у вас есть на компе, на котором вы приложение запустили. Соответственно, когда у вас, в общем-то, испытывает трудности лента новостей, то испытывает трудности на самом деле всё ваше приложение, потому что если горит лента новостей, то горит весь сервер. А поскольку у вас единый монолит и все остальные фичи запущены на том же самом сервере, в том же самом приложении, то ресурсов начинает не доставать, и всем им, так как лента новостей забирает на себя все остальные, потому что ей нужно поддержать вот этот рост трафика, который там в какой-то ситуации появился.
Логичная стратегия в этой ситуации — это взять ваше приложение, вот этот монолит, поставить ещё дополнительных серверов, тоже купить их за 3.000 долларов, несколько новых серверов и копию этого приложения запустить не на одном сервере, а на сразу всех, которые вам доступны. Таким образом, вы ставите несколько серверов, у вас несколько копий приложения запущенных на разных серверах, и все запросы пользователей вы можете распределить равномерно по всем этим доступным серверам с помощью какой-нибудь программы, которая называется лоадбалансер, или балансировщик нагрузки. Например, это может быть программа Nginx. Так вот, кажется, что проблема решена. Но на самом деле, у вас сложности испытывал компонент ленты новостей. Все остальные компоненты в вашем приложении не были нагружены. Но вы из-за того, что это одно большое приложение, и для его работы, для всего большого приложения нужен очень мощный, дорогой сервер, вы как бы расширяя ваше приложение, тратите деньги на то, чтобы поддержать работу одной функции, а не всех. Все остальные хорошо работали, и это дорого.
Современные компании хотят сэкономить ресурсы, они не хотят тратить столько денег на поддержку всей вот этой инфраструктуры, а хотят этот процесс удешевить очень сильно, да? И они думают: "А если у нас нагружается только одна фича из четырёх, допустим, которая у нас есть в монолите, как нам сделать так, чтобы когда нам нужно добавить новых серверов, то мы тратили деньги только на эту фичу, а не на все сразу?" Но монолит это не позволяет делать, потому что это единый кусок, единое приложение. Для всего его запуска нужен огромный мощный сервер. Тут уж никуда не денешься.
Ну и когда современные приложения стали настолько большими, что с этими вышеописанными проблемами стало уже невозможно просто справляться, то люди придумали технологию микросервисов. Что они сделали? Очень всё просто на самом деле. Они посмотрели на свой монолит и увидели, что он состоит из нескольких частей, которые как бы обособлены, но из-за архитектуры, как бы внесения их в один-единственный проект и содержания их в единой кодовой базе, они жёстко друг с другом связаны. И люди такие подумали: "А что, если мы возьмём логику каждого компонента в отдельности и вместо того, чтобы хранить их как единый проект, прямо создадим четыре отдельных программы, четыре отдельных там, например, Java-приложения на Spring, Git-репозитории, всё будет отдельное. И мы просто разработку каждого из этих компонентов будем вести в отдельном проекте, и код будет посвящён в этом проекте только одной фиче: создание постов, код, относящийся только к созданию постов, лента новостей — только к ленте новостей, и ничего больше". И каждое из этих приложений потом мы будем запускать на своём отдельном сервере, то есть буквально на отдельном компьютере, отдельном от всех остальных.
Но поскольку эти компоненты всё ещё должны взаимодействовать между друг другом для того, чтобы приложение это формировалось как единая система, то мы организуем вот взаимодействие это просто по интернету с помощью запросов от одного приложения к другому по интернету, там протокол HTTP, например, или с помощью дополнительных программ, таких как Kafka. Kafka обеспечивает взаимодействие, обмен информацией между собой для разных приложений, запущенных на разных компьютерах. Это для чего Kafka нужна? Ну, о ней мы поговорим отдельно.
В итоге получилось, что у вас всё ещё есть одна большая система, одно большое приложение, которое выполняет всё те же самые функции, но обмен информацией между разными компонентами вместо буквально вызова методов и функций, вместо взаимодействия на уровне кода каких-то систем в рамках одного проекта, теперь происходит по интернету с помощью вот связей, организованных по другим, как бы, каналам взаимодействия. Тем не менее, это всё ещё была и остаётся единая система. Но каждый компонент в ней существует изолировано от всех остальных. И поскольку каждый компонент — это, по сути, отдельное приложение, отдельная программа, служащая каким-то целям, а услуги, да, обслуживание по-английски — это слово "Service", а само вот это приложение, один кусочек, он гораздо меньше, чем весь монолит, то такие отдельные приложения стали называть микросервисами, потому что они предоставляют какие-то услуги тем компонентам, которые от них зависят, или пользователям, а ещё они гораздо меньше, чем монолит. Тем не менее, любой микросервис — это не обязательно маленькая программа. В ней может быть очень-очень много кода, но она гораздо меньше, чем вся её окружающая система. И, кроме того, этот код посвящён одной какой-то специальной функции, одной какой-то доменной области и не производит никаких других действий. Он отвечает за конкретную вещь.
В результате у такой архитектуры появилось огромное количество преимуществ по сравнению с монолитами. Одно из них — это, например, гибкость разработки. Теперь, если я как программист хочу снова внести изменения в мою ленту новостей, в общем-то, то я просто открываю микросервис ленты новостей и вношу туда свои изменения, меняю её код. Но поскольку этот компонент теперь не имеет жёсткой связи с другими компонентами, он к ним обращается по интернету, он их код вообще не видит, не знает, как он выглядит, и никак с ним не взаимодействует, он отправляет запросы по интернету. Теперь, изменяя мой компонент ленты новостей, я вообще никак не изменяю окружающие компоненты, я не могу на них повлиять никак с точки зрения кода. Я должен протестировать только свои изменения в этом микросервисе, убедиться, что они работают корректно, а все остальные микросервисы не были изменены, в них код остался тем же самым, а значит, и багов в них не может быть, потому что любое изменение может привести к багам. Но если нет изменения, то нет и вероятности появления багов, да? И в итоге такая разработка стала гораздо более гибкой. Я гораздо быстрее теперь могу вносить изменения в мою кодовую базу, которая очень большая, из-за того, что я вношу эти изменения в один изолированный компонент и не затрагивающее качество моей системы стало выше, я лучше уверен в том, что всё работает корректно, и мне просто нужно делать меньше работы. Раньше мне приходилось все компоненты обновлять, теперь я вношу только изолированные изменения в мой компонент, и всё дело сделано.
Более того, ваша система стала гибкой в том отношении, что каждый микросервис — это, по факту, отдельное приложение. Если, например, какой-то из этих микросервисов выполняет определённую логику, для которой лучше подходят другие технологии, чем те, которые вы используете в остальных микросервисах. Например, вы используете Java и Spring, но для разработки системы рекомендаций, которая как бы использует Machine Learning, лучше подходит там Python, например, потому что весь Machine Learning сегодня на Python, и там какая-нибудь библиотека PyTorch, по-моему, называется, для этого машинного обучения. Мы можем вот это отдельное приложение, отдельный микросервис для системы рекомендаций написать не на Java, а на Python, и всё ещё наша система будет работать корректно. То есть у нас
Появляется разнообразие в выборе технологий, которые лучше подходят для тех или иных задач, и наша разработка становится более гибкой. Наша архитектура становится более гибкой. Мы можем делать множество разных вещей, не завязываясь на тот инструмент, который мы используем во всех остальных местах.
Ну и самое классное — это то, что теперь любой другой разработчик, который тоже хочет внести свои изменения, но, например, в другой компонент. Да, например, в систему рекомендаций. У вас есть какой-нибудь другой программист, который занимается машинным обучением в команде, он приходит и обновляет код системы рекомендаций. Эти изменения вообще никак не связаны с вашими, они существуют изолированно, вообще в другом приложении. Вы даже как разработчик можете не знать, что это приложение существует во всей вашей системе, потому что вы там занимаетесь Java-разработкой. Этот человек занимается машинным обучением, это его вообще стезя, область, и вам об этом даже думать не надо. Всё это происходит изолированно. Его изменения не могут затронуть ваши, а ваши не могут затронуть его. Это очень-очень удобно. Это ускоряет и упрощает работу над такой огромной, сложной системой для множества программистов, которые вот в одной команде сосуществуют.
Ну и соответственно, это приводит вас к ещё одному замечательному преимуществу — это преимущество изоляции микросервисов друг от друга. И особенно это хорошо видно вот при деплое этих микросервисов. Например, вы как программист обновили код ленты новостей в нашей социальной сети на Java и Spring, а другой член вашей команды обновил систему рекомендаций на Python. Поскольку это два разных приложения, то вы, в общем-то, будете ваше приложение, микросервис ленты новостей, собирать с помощью технологии Gradle в свой исполняемый JAR-файл, а приложение, микросервис системы рекомендаций на Python, будет собираться вообще другим инструментом, относящимся к Python, вообще в другой файл, да, новой версии, которая вот с этим изменением.
И вам теперь вот эти новые версии ваших микросервисов нужно доставить пользователям, нужно задеплоить на сервера, чтобы пользователи могли с ними взаимодействовать. Но обратите внимание, у вас теперь два отдельных друг от друга исполняемых файла с разными изменениями. Они могут быть даже разных версий, в зависимости от того, как часто меняются эти микросервисы, о которых мы говорим.
И в итоге теперь с микросервисами у вас вот такая вот архитектура. Весь ваш монолит разъехался на множество других микросервисов, на составные части, которые взаимодействуют между друг другом и запущены на отдельных серверах. Теперь, когда вы делаете деплой, то есть выкатываете новую версию для пользователей, вы запускаете новый сервер, на который засовываете этот исполняемый файл, запускаете приложение новой версии, переводите трафик пользователей на этот новый сервер, а старый выключаете. То же самое вы делаете и для другого микросервиса на совершенно отдельном сервере с совершенно отдельным файлом, и трафик пользователей перенаправляет вообще через другую конфигурацию. Всё изолировано друг от друга.
Но теперь представьте, что возникает та же самая ситуация, которая была до этого. Machine Learning engineer ошибся в своём коде, и система рекомендаций стала содержать баг. Юзеры, в общем-то, недовольны, он ошибся, всё такое, и нужно сделать откат к предыдущей версии. Получается, он берёт просто свой микросервис, ставит на сервера, в общем-то, старую версию приложения, старый исполняемый файл, переводит трафик на старую версию, и всё. Ваш микросервис как был в новой версии, так и остался. У вас-то багов нет. Это полностью отдельное приложение, его не пришлось откатывать. Таким образом, из-за того, что изменения изолированы, деплой и откат, всё взаимодействие с вашими микросервисами, оно изолировано. И таким образом вы больше не связаны с другими людьми, когда у них происходят какие-то проблемы. У вас нет. Вы всё ещё можете предоставлять своим пользователям качественный функционал, даже если какая-то часть вашей системы работает нестабильно, другие части работают стабильно. И это гораздо лучше, чем всё у пользователей забирать. Это огромное преимущество микросервисов, потому что ваши пользователи любят и они платят за эти приложения, чтобы в них был богатый функционал. Если вы периодически забираете у них новые фичи, они будут очень расстроены. А теперь вы забираете у них только поломанные фичи, а те, которые работают, тут, в общем-то, оставляйте с ними. И вы не имеете этой зависимости больше. И изолированность микросервисов — это супер круто в этом отношении.
Как решается теперь проблема масштабирования с микросервисами? Поскольку мы сказали, что каждый микросервис — это полностью отдельное приложение, которое запускается на полностью отдельном компьютере от всех остальных, то представьте теперь ситуацию, что у нас снова выросла нагрузка на фичу ленты новостей. Получается, что нагрузка выросла на самом деле на наш микросервис ленты новостей и на сервер, на котором этот микросервис запущен. Только на него, только этот сервер испытывает нагрузку. И более того, поскольку эти приложения гораздо меньше, да, с точки зрения своего функционала, поскольку они существуют отдельно друг от друга, то сервера, которые нужны для того, чтобы каждый микросервис на них запустить, нужны более дешёвые, потому что просто приложение меньше, оно использует меньше ресурсов. Значит, сервер будет стоить там не 3000, а 1000 долларов. Это уже дешевле, да, запускать на нём, в общем-то, всё ваше приложение.
Но более того, теперь, когда нагрузка на ваш микросервис выросла значительно, то вы должны поставить новые сервера, чтобы дать больше ресурсов этому микросервису, да, запустить его копии на этих серверах, чтобы, в общем-то, поддержать вот эту нагрузку, чтобы её всю обработать, потому что, ну, вот трафик вырос. Ну, какие сервера вы будете ставить для этого микросервиса? Такие же, на которых он сейчас запущен. А это гораздо более дешёвый сервер. То есть вы масштабируете только его, а остальные, потому что они не испытали, например, в этот момент нагрузки, вы их не трогаете. Получается, мало того, что сервера дешевле, их нужно ещё и меньше, да. И таким образом компания, которая вот этим всем занимается, она экономит огромное количество денег на этом, потому что трафик постоянно волнообразно может расти в разных местах системы, и вы можете гибко масштабировать эту систему. Если лента новостей испытывает нагрузку, это не значит, что система нотификации испытывает нагрузку. Я добавляю сервера для ленты новостей, но система нотификации продолжает работать на том же наборе серверов, и я не трачу туда дополнительные средства. Это очень-очень выгодно для компании.
И вот именно поэтому современные большие компании в основном переходят на микросервисы, потому что для них это просто дешевле, намного, намного дешевле, чем иметь один монолит, который нужно масштабировать. А современные приложения они огромные, а значит, и нужно много серверов и очень-очень дорогих. И микросервисы решают эту проблему, сильно удешевляют поддержку работы вашей системы.
Кроме того, если говорить про удешевление разработки, то тоже микросервисы великолепно влияют на вообще команду вот программистов, которая развивает это приложение. Теперь, поскольку у вас каждый микросервис — это отдельная программа, вообще существующая в изоляции от всех остальных, то вы можете сказать, что одна команда чит занимается одним приложением, вторая команда другим, третья третьим. И знать, эти разработчики должны только про свой микросервис. Они должны великолепно понимать, как он работает, они должны знать, как его, в общем-то, улучшать и так далее, и так далее. Но им не нужно вообще погружаться в то, как устроен код в других микросервисах. В итоге им, во-первых, легче разбираться, легче. Им не нужно думать про кучу связей и тратить множество времени. Они быстрее доставляют фичи, значит, бизнес развивается быстрее и многое другое. Да, просто разрабатывать такую систему в коллективе становится сильно проще, потому что каждый инженер концентрируется на том, на чём он должен концентрироваться и отвечает только за часть функционала, а не за всё сразу.
Ну и, конечно, когда вы используете микросервисы, повышается значительно надёжность вашей системы. Представьте себе ситуацию, что у вас был монолит, и вдруг сервер, на котором этот монолит запущен, это же железный компьютер, выключился. Не знаю, его выключили из розетки, или на нём сгорел процессор, что угодно. Ваша программа, этот монолит, выключается, и все фичи вашего приложения перестают работать, и пользователь в ярости. Но в случае микросервисов, например, у вас сломается компьютер, сервер, на котором запущен микросервис отправки нотификаций, предположим, все серверы ломаются, даже весь кластер серверов, и ваш микросервис нотификации превращается буквально в тыкву, да, с ним ничего нельзя сделать. Он перестаёт нотификации отправлять, он просто не работает. Все компьютеры, на которых он запущен, выключились. Но поскольку это отдельный микросервис, это отдельные компьютеры, никак не связанные с компьютерами для других микросервисов, то другие части вашей системы продолжают работать. Ваше приложение буквально немножко деградировало, в нём отвалились нотификации, но все остальные фичи продолжают работать для пользователей корректно. Точно так же, как и для этого. В итоге они могут заметить какое-то там падение, недостаток, но это не всё приложение отключилось. Оно продолжает функционировать. Это очень важно. Лучше потерять часть какой-то системы, чем всю систему, потому что из всех остальных частей может идти огромный поток прибыли, допустим, или ещё чего-то, да, показа рекламы, просмотры и так далее, и так далее. И тем не менее, при микросервисной архитектуре ваша система, в общем-то, остаётся в рабочем состоянии, пока вы не почините один из её упавших компонентов. Люди всё ещё, некоторые даже могут не заметить, что один из этих компонентов упал, да. И это очень-очень круто.
А теперь вопрос, который волнует сегодня множество начинающих разработчиков: если я разрабатываю свой проект, то что мне использовать — монолит или микросервис? Есть два варианта развития событий. Если вы прямо разрабатываете стартап и хотите прям реально сделать приложение, которое будет потенциально зарабатывать деньги, вы придумали бизнес-план, все дела, у вас есть даже команда, которая вас поддержит, то начинайте с монолита, потому что вам будет гораздо проще его разрабатывать. Делать микросервисы — это гораздо более сложная задача архитектурно, и она вас сильно замедлит при разработке стартапа. Монолит можно разрабатывать гораздо проще, потому что вы не переживаете по поводу организации вот этих связей и распределения данных по вашей системе. На уровне стартапа, где очень важно доставлять как можно быстрее новый функционал, это вот скорость разработки будет выше в ситуации монолита. А потом, когда вы придёте к ситуации, что ваш стартап стал супер большим приложением, и разрабатывать его уже становится сложнее из-за того, что это монолит, вы должны его распиливать на микросервисы.
Но если вы разрабатываете pet-проект для того, чтобы произвести впечатление на работодателя, показать что-то в своём резюме и сделать круто, то стоит инвестировать в микросервисы своё время, учиться их разрабатывать, потому что все современные компании, большие, куда вы хотите попасть, они используют микросервисы на работе. Если вы им сможете при трудоустройстве показать, что вы уже умеете делать то, чем они занимаются прямо сейчас, то с большей вероятностью вас возьмут, конечно, на работу. Другое дело, что разрабатывать микросервисы сильно сложнее, чем монолит, и, конечно, в этом отношении вам помогла бы поддержка опытного наставника, поддержка человека, который может для вас сформировать чёткую архитектуру на базе микросервисов. Также, чтобы человек проводил ревью кода, который вы пишете в этом отношении. А ещё было бы здорово, если бы у вас была команда разработки, с которой бы вместе вы делали весь этот сложный проект из нескольких микросервисов, чтобы добиться максимально полного и выдающегося результата, который потом не стыдно людям показывать. Всё это — вот наставника, код-ревью, крутые лекции по микросервисам, классные фичи на микросервисах с Kafka и Redis, и команду, в которой вы все вместе будете пилить этот один большой проект на девяти микросервисах сразу на и Spring с использованием всех самых современных технологий — всё это вы можете получить на моём Java-буткемпе по ссылке в описании. Именно там люди благодаря вот такому замечательному проекту получают работу в лучших компаниях в индустрии, таких как Сбер, Яндекс, ВКонтакте, Тинькофф, и многие-многие другие, а на зарплату сразу от 100 до 200.000 в месяц на джуна, при том, что люди получают свою первую работу именно потому, что они делают суперкрутой проект, добавляют его в своё портфолио, там микросервисы, там все самые современные технологии, классные фичи, и всё это при поддержке супер-опытных на буткемпе. Ссылка в описании.
Ну что ж, теперь поговорим о проблемах микросервисов, потому что они, очевидно, достаются не бесплатно. Разрабатывать микросервисную архитектуру гораздо сложнее, чем монолитную. Это 100%. И именно поэтому сегодня требования для разработчиков возрастают, потому что это просто гораздо более сложная разработка. И вот почему. Посмотрите на ту архитектуру, которая у нас была раньше при монолите. У нас был фронтенд, у нас был бэкенд, одно приложение, запущенное на сервере, у нас была база данных. Вот и всё, в общем-то, что у нас было. Всё работало великолепно. А потом мы решили, ну, на фронтенд, в общем-то, наш монолит распилить на микросервисы. И вот что у нас получилось. Мало того, что у нас теперь четыре приложения отдельных вместо одного, который нужно запускать на отдельных серверах, между ними нужно организовать взаимодействие по интернету. Каждый из этих микросервисов должен знать, по какому адресу в интернете находятся все остальные, чтобы с ними связаться, как-то отправить им данные, от них данные получить. Более того, там, возможно, для взаимодействия где-то используется не только обычный протокол HTTP, но и Kafka какая-нибудь, которая тоже отдельная программа, которую нужно поднимать на отдельном сервере. Да, и всё это ещё взаимодействует с этой нашей базой данных. То есть, если вы на это просто посмотрите, то уже при четырёх микросервисах архитектура нашей системы стала гораздо сложнее. Её нужно администрировать, за ней нужно следить за каждым отдельным микросервисом. Любой из этих серверов может упасть, за ними надо наблюдать, анализировать, что происходит в реальном времени. А современные системы могут содержать в себе не просто четыре микросервиса, а сотни микросервисов, более того, написанных на разных технологиях, использующих разные конфигурации запуска и так далее, и так далее. Ваша система, её архитектура и поддержка становится сильно сложнее при использовании микросервисов. Банально сложнее об этом беседовать, рассуждать о том, как что-то работает.
Также есть потенциальная проблема с коммуникацией, поскольку ваши микросервисы теперь не взаимодействуют напрямую, а взаимодействуют между собой по интернету, то это может приводить, в общем-то, к ошибкам. Почему? Потому что передача данных по интернету ненадёжна. Сети, ни одна сеть не является надёжной. Они никогда не гарантируют вам стопроцентной гарантии доставки тех данных, которые вы по ним отправили. Что-то может прерваться на середине, да. И таким образом, когда ваши микросервисы взаимодействуют между собой, вы должны как разработчик быть постоянно готовы к тому, что вот этот запрос по интернету может на середине отвалиться, или сам запрос может быть доставлен, а ответ вы можете не получить, потому что он тоже обратно по интернету передаётся. И вы должны в своих микросервисах убеждаться, что если эта ситуация произошла, вы должны сделать дополнительные попытки, попробовать ещё раз и так далее, и так далее. Ещё при передаче данных по интернету, они же могут, ну, когда ошибка происходит, вообще потеряться, вся информация исчезнет. И если вы, например, не убедились в том, что какие-то критические данные, передавая по интернету, не исчезли из-за каких-то проблем сетевых, то вы, в общем-то, потерпели крах. То есть вы как разработчик должны придумывать способы не потерять эти данные. Именно для этого, например, использует Kafka, потому что она гарантирует, что данные не потеряются, да, в частности. Но это вообще другая история, об этом мы поговорим в отдельном видео подробном про Kafka.
Ещё одна важная проблема, которая может существовать в микросервисах, — это проблема согласованности данных. Представьте себе ситуацию, что какой-нибудь пользователь заходит в приложение, вводит какие-то данные через поле ввода и отправляет их на ваш бэкенд. Ваш бэкенд состоит из кучи микросервисов. Да, эти данные там приходят, например, в сервис постов, потом они сохраняются из этого сервиса постов в базу данных. Но кроме того, этот микросервис постов ещё и отправляет сообщение, там, допустим, в Kafka с какой-то информацией об этом посте, и операция выполнена. Микросервис нотификации, например, потребляет на другой стороне Kafka эту информацию. Но может возникнуть такая ситуация, если вы таким образом написали код, что потребление данных из Kafka произойдёт раньше, чем запись в микросервис постов данных, а микросервис нотификации с данными, которые он получил из Kafka, попробует обратиться в базу данных и обновить или получить что-то из базы данных, в общем-то, ожидая, что микросервис постов уже туда что-то сохранил или там данные находится в каком-то определённом состоянии. Ну, это очень простое моделирование проблем, и получится, что какой-то информации ещё нет в базе данных, но вы уже из другого микросервиса пытаетесь к ним обратиться, а их там нет. Вы получаете ошибку, и значит, ваш ша система не синхронизирована с точки зрения компонентов между собой, и значит, данные, которые находятся в вашей базе, они не являются согласованными для того, чтобы ваше приложение работало правильно, именно в отношении его логики. И это очень сложная проблема, она может вылазить где угодно. Это вот простая ситуация. Если у вас SQL-база данных, скорее всего, вы всё сделаете в транзакции, и у вас всё будет в порядке в данной ситуации. Но если вы используете, например, NoSQL-базу данных, то там ситуация сильно сложнее становится, да, потому что в SQL-базах данных нет транзакций. И тем не менее, вот в микросервисах, из-за того, что они могут обращаться из разных мест к одному и тому же источнику данных, а могут возникать проблемы с согласованностью, что вы ожидаете что-то получить, но не получаете этого. И разработчики об этом должны думать. Именно поэтому разрабатывать микросервисы становится сильно сложнее. Более того, вообще по канонам, у каждого микросервиса должна быть своя отдельная база данных, никак не связанная со всеми остальными. И здесь проблема доступа к данным становится ещё сложнее. Что если вам нужны в одном микросервисе данные, которые на самом деле хранятся в базе другого микросервиса? А ещё вам нужно обновить данные сразу в нескольких базах, а транзакции ACID, они распространяются только на одну базу данных. Как выполнить транзакционное обновление сразу в нескольких базах данных? Тут появляются всякие паттерны типа Saga, там, короче, разработка становится гораздо, гораздо сложнее, когда вы начинаете думать об этих проблемах. Именно поэтому разработчики сегодня получают столько денег, потому что это сложные проблемы, которые им приходится решать.
Ну и самое главное, что меня как разработчика в микросервисах вымешивает больше всего, — это дебаг. Да, вот у нас была раньше архитектура с монолитом. Пользователь там клацает какую-то кнопку на фронтенде, у него появляется огромная красная ошибка. Он нам пишет, говорит: "У меня ошибка в приложении, почините". У нас было два варианта, где эта ошибка могла возникнуть в нашей системе: она либо на бэкенде в нашем приложении единственном, либо где-то в базе данных. Да, мы логи проверяли на бэкенде, смотрели, что там в базе данных, и находили эту проблему, в общем-то. А потом мы перешли на монолиты, где куча компонентов. И когда у пользователя возникает ошибка на фронтенде, он нам говорит: "У меня ошибка, почините". То вы на это всё смотрите: а где конкретно возникла эта проблема? Да, его операция прошлась через всю нашу систему, он взаимодействовал сразу с группой компонентов. В каком из них эта проблема возникла? Туда мне надо посмотреть или сюда, в этот микросервис, или в другой, или в базу данных, или в лоад-балансер, или в Kafka, что пошло не так? Да, у меня становится гораздо больше точек отказа в моей системе, о которых я должен думать. А если я хочу ещё и про дебажить, вы буквально ставите брейкпоинт в вашем приложении, а потом получается, что исполнение улетает вообще в другой микросервис, и оно будет выполняться в другом потоке в этом микросервисе, потому что это вообще отдельный компьютер, и вообще всё будет по-другому. И как вам отследить, что этот запрос один и тот же, пролетающий через всю систему? Там появляется логика добавления трейсинга этих запросов и так далее, и так далее. Ещё сильнее разработка усложняется. Это тоже большая проблема. Существуют отдельные библиотеки для того, чтобы, в общем-то, трейсы отслеживать запросы, проходящие через всю вашу систему, но так или иначе, про всё это нужно читать, со всем этим нужно разбираться, анализировать, учиться с этим, в общем-то, работать, справляться с такими проблемами. Как раз поэтому, как я уже и сказал, разработчики являются теми, кем они являются: сильными инженерами, которые работают в классных компаниях, получают кучу бенефитов и много денег, потому что они тратят сначала время на исследование сложных проблем, на то, чтобы научиться с ними работать, и потом просто решают вот такие задачи высокой сложности для супермощных, огромных компаний. И чтобы научиться это всё, приходите на Java-буткемп по ссылке в описании. Будет круто.
Ну, а я надеюсь, что это видео было очень ценным для вас. Будет здорово, если поставите лайк, подписывайтесь на канал, это правда помогает. Также приходите в мой Telegram-канал по ссылке в описании, где вы найдёте огромное количество суперкрутых также подробных технических постов с классными анимациями, и вообще всё объяснено простыми словами на самые разные темы в разработке. Будет очень круто, подписывайтесь на Telegram-канал. Спасибо вам и увидимся.