Найти в Дзене
Максим Чернышев

Микросервисы мертвы - да здравствуют минисервисы

Предупреждение: это будет одна из тех статей для пуристов, которые объясняют, что вы делаете не то, что думаете, просто потому, что на самом деле не знаете полного определения того, что вы думаете, что делаете. Если вы согласны с этим, тогда мы можем продолжить. Вы когда-нибудь определяли или внедряли архитектуру на основе микросервисов? Скорее всего, вы ошибаетесь. Извините, но сегодня я выступаю в роли "полиции определений". Скорее всего, вы имеете дело не с микросервисами, а с минисервисами: Минисервисы. Давайте попробуем объяснить, почему это так и почему нормально ошибаться в этом вопросе. Микросервисы, минисервисы - все это маленькие сервисы, не так ли? То есть, да, вы не ошибаетесь в этом, на самом деле, путаница происходит не здесь. Мы склонны думать о "микросервисах" как о небольших, очень сфокусированных на логике сервисах, которые занимаются, как правило, одной задачей. Однако, если мы посмотрим на определение микросервисов Мартина Фаулера - а вы знаете, он очень умный парен

Предупреждение: это будет одна из тех статей для пуристов, которые объясняют, что вы делаете не то, что думаете, просто потому, что на самом деле не знаете полного определения того, что вы думаете, что делаете.

Если вы согласны с этим, тогда мы можем продолжить.

Вы когда-нибудь определяли или внедряли архитектуру на основе микросервисов? Скорее всего, вы ошибаетесь. Извините, но сегодня я выступаю в роли "полиции определений".

Скорее всего, вы имеете дело не с микросервисами, а с минисервисами: Минисервисы. Давайте попробуем объяснить, почему это так и почему нормально ошибаться в этом вопросе.

Микросервисы, минисервисы - все это маленькие сервисы, не так ли?

То есть, да, вы не ошибаетесь в этом, на самом деле, путаница происходит не здесь.

Мы склонны думать о "микросервисах" как о небольших, очень сфокусированных на логике сервисах, которые занимаются, как правило, одной задачей. Однако, если мы посмотрим на определение микросервисов Мартина Фаулера - а вы знаете, он очень умный парень, поэтому нам обычно нравится знать, что он думает - вы заметите, что мы упускаем очень маленькую, но ключевую черту микросервисов: развязанность.

Давайте подробнее рассмотрим то, что мы называем "микросервисом". В наши дни этим термином так часто бросаются, что это уже напоминает подростковый секс: все об этом говорят, никто толком не знает, как это делается, все думают, что все остальные это делают, поэтому все утверждают, что они это делают.

По правде говоря, в 99% интервью, которые я беру как менеджер, на вопрос о микросервисах я получаю ответы о REST API. И нет, это не обязательно одно и то же.

И по определению, REST API сами по себе не могут быть микросервисами, даже если вы разделите их на несколько более мелких, каждый из которых будет отвечать за одну задачу. Они не могут, потому что по определению для того, чтобы вы могли использовать REST API напрямую, вы должны знать о нем.

Попутно замечу: существует два типа REST-разработчиков (то есть разработчиков, создающих REST API):

Те, которые реализуют столько функций REST, сколько, по их мнению, им нужно. Это означает, что они могут заботиться о ресурсно-ориентированных URL, если им так хочется, и, возможно, они будут беспокоиться о том, чтобы их API были stateless, поскольку это не так уж сложно реализовать. Но этот тип разработчиков в 99,9% случаев откажется от HATEOAS (Hypermedia As The Engine of Application State). Другими словами, самообнаруживаемость структуры API больше не является особенностью, между клиентом и сервером существует жестко прописанный контракт.

Те, которые следуют стандарту REST до буквы. Мне кажется, что за весь свой опыт я видел только одного такого разработчика - так что если вы такой же, пожалуйста, оставьте комментарий и давайте общаться! - . Реализация REST API таким образом может занять гораздо больше времени, но результат намного лучше, особенно потому, что клиенты имеют очень мало связей с сервером, все, что им нужно знать о том, где он находится и какова корневая конечная точка. Остальное делается через самообнаружение, очень круто.Однако в обоих случаях связь между клиентом и сервером все еще существует. Вы не можете получить разобщенную коммуникацию ТОЛЬКО через REST, и именно поэтому, если мы строго придерживаемся определения микросервиса - а мы пытаемся придерживаться - их нельзя называть именно так.

Поэтому вместо этого давайте ослабим пояс определения, оставим себе некоторую свободу действий и назовем эти сервисы минисервисами.

Итак, мы можем определить минисервис как:

Достаточно маленькие сервисы (обычно API), чтобы считаться "мини", они все еще должны заботиться только об одной единственной ответственности, однако мы не слишком беспокоимся о части "развязки с клиентом".

Конечно, чем меньше они связаны, тем лучше для будущих изменений, наличие динамического клиента определенно поможет адаптироваться быстрее и без особого влияния на изменения на сервере. Это определенно плюс, но это не часть нашего определения.

Совет: Создавайте приложения из независимых компонентов для скорости и масштабирования

Вместо того чтобы создавать монолитные приложения, сначала создавайте независимые компоненты и объединяйте их в сервисы и приложения. Это ускоряет разработку и помогает командам создавать более последовательные и масштабируемые приложения.

Инструменты OSS, такие как Bit, предлагают отличный опыт разработчика для создания независимых компонентов и компоновки приложений. Многие команды начинают с создания своих систем проектирования или микрофронтендов на основе независимых компонентов.

Попробуйте →

Независимый компонент "карточка" с исходным кодом и общим доступом. Справа => его граф зависимостей, автоматически сгенерированный Bit.

Что же такое микросервис?

Если мы хотим быть технически подкованными, то по определению мы все еще имеем дело с сервисом, который работает с одной ответственностью, но в то же время он способен быть отделен от любого клиента, который его потребляет.

Как же добиться такого разделения? Хитрость заключается в том, чтобы думать вне сервиса: о канале связи.

Мы склонны считать, что микросервис = REST API, и в то же время REST API автоматически ассоциируется с парадигмой связи клиент-сервер. Вот так, за долю секунды мы перешли от микросервисов к клиент-серверу. Но давайте немного отмотаем назад.

Некоторое время назад я писал о своих любимых паттернах межсервисного взаимодействия, и там я перечислил один конкретный вариант: Асинхронный обмен сообщениями.

Этот вариант не соединяет напрямую клиента и наши сервисы, вместо этого требуется центральная шина сообщений, которая заботится о доставке сообщений туда и обратно между клиентом и сервисами, при этом они никогда не должны знать друг о друге. Вы видите, что мы только что получили? Правильно, мы полностью разделили клиента и сервер с помощью простой смены парадигмы.

-2

Посмотрите на приведенную выше диаграмму: клиентское приложение запускает потенциальный мультисервисный запрос с помощью одного сообщения. Ему даже не нужно знать, сколько сервисов задействовано, что просто фантастика. Это означает, что оркестровка сложных последовательностей запросов больше не входит в обязанности клиента. Это очень важно!

В конечном итоге клиентское приложение получает результат, который оно искало. И я говорю "в конечном итоге", потому что это не синхронная модель коммуникации, учитывая то, как она работает, она должна быть асинхронной. Это не обязательно проблема, это просто требует изменений в том, как мы кодируем наших клиентов (и другие микросервисы тоже, учитывая, что так они тоже могут общаться друг с другом).

Давайте на секунду задумаемся: коммуникационная модель на основе шины сообщений - это не только то, что позволяет вам правильно называть свои сервисы "микросервисами", она также дает некоторые приятные преимущества:

Вся ваша архитектура, включая клиентские приложения, становится реактивной. Это, на мой взгляд, фантастическое преимущество, особенно если вы имеете дело с запросами, которые по своей природе требуют длительного времени для решения. Возможно, вы имеете дело с очень сложными вычислениями, запускаете некоторые ML-модели или просто собираете данные из API других сторонних производителей. Какой бы ни была причина, ваши клиенты могут продолжать заниматься чем-то другим в ожидании ответа, вместо того, чтобы держать активное соединение в надежде, что оно не прервется. На самом деле, в этом докладе, который я недавно прочитал, я рассказываю о некоторых преимуществах реактивной архитектуры и о том, как ее можно создать с помощью Redis:Горизонтальное масштабирование сервисов очень просто. В случае связанной реализации, например, когда REST API напрямую общаются с клиентом, вам потребуется какой-то балансировщик нагрузки или шлюз API, который позволит вам распределить нагрузку между всеми копиями одного и того же минисервиса. Но если мы имеем дело с развязанной архитектурой, нам не нужно беспокоиться об этом, первый микросервис, получивший сообщение, позаботится о нем, оставляя копии свободными, чтобы позаботиться о следующем входящем сообщении.

Добавление новых сервисов не оказывает прямого влияния на клиента. Это жестоко, если вы спросите меня. Потому что при взаимодействии клиент-сервер добавление новой службы (либо потому, что вы добавляете новую функцию, либо потому, что вы решили разделить существующую службу) означает, что клиенту теперь нужно знать, к кому обращаться при различных сценариях. Либо вы перенесли оркестровку в центральный API, куда попадают все запросы, однако в этом случае вам также придется обновить логику оркестровки на вашем сервисе. Конечно, на клиента это не влияет, но все равно возникает побочный эффект. В архитектуре, основанной на микросервисах, это уже не так. Оркестровка выполняется шиной сообщений, что делает добавление почти без последствий.

Более простая повторная попытка и более устойчивая архитектура. Это два очень связанных момента. С одной стороны, вы должны беспокоиться о том, что произойдет, если ваши сервисы по какой-либо причине умрут (или, по крайней мере, будут недоступны). Для коммуникации клиент-сервер это означает, что запросы не будут выполняться. Однако для шины сообщений это означает, что запрос не будет выполнен до тех пор, пока службы не восстановятся. Шина сообщений может хранить сообщения в течение некоторого времени, что позволяет создать более устойчивую архитектуру. В то же время, если вы хотите иметь какую-то логику повторной попытки в случае сбоя, как я уже говорил, это подразумевается для архитектуры на основе микросервисов, в то время как для клиент-серверной архитектуры вам придется кодировать ее самостоятельно.

Еще не убедились? Правда? Тогда, возможно, это потому, что ваш сценарий использования не идеально подходит для микросервисов. Возможно, вам подойдут мини-сервисы.

Главное, что нужно вынести из этой статьи, - понять, что хотя "формально известные микросервисы" могут быть прекрасны, они не являются обязательными для каждой архитектуры.

Все дело в компромиссах. Микросервисы могут потребовать гораздо больше работы на уровне инфраструктуры и, возможно, даже усиления вашей команды разработчиков для правильного подхода к асинхронному взаимодействию. Однако конечный продукт может дать гораздо лучшие результаты и гораздо меньше головной боли в долгосрочной перспективе.

Не существует архитектуры "серебряной пули", которая решит проблемы каждого, поэтому не воспринимайте определение микросервисов, данное этим фанатиком, как то, что я говорю вам отказаться от клиент-серверного подхода. Это не может быть дальше от моих намерений.

Эта статья призвана еще раз показать, что модель взаимодействия клиент-сервер, хотя она и является наиболее распространенной и более простой в реализации, не обязательно является лучшей.

А что думаете вы? Готовы ли вы наконец попробовать микросервисы для разнообразия? Или вы пока будете придерживаться мини-сервисов?