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

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

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

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

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

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

Также пробежимся кратко по недостаткам, которых в целом не так много, но некоторые из них могут показаться вам достаточно критичными:

  1. Сложность. Сам по себе сервис организовать не сложно, сложно заставить всю систему работать согласованно.

  2. Тестирование. Сложность написания тестов возрастает, если сервисы зависят друг от друга.

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

  4. Задержка сети. Критично не для всех проектов, но если у вас идет длинная цепочка зависимостей на запросы в API при межсервисных взаимодействиях, то появляется лишняя задержка, которую необходимо будет постараться оптимизировать.

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

Паттерны, которые нужно знать на собеседовании

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

Давайте рассмотрим в этом разделе несколько паттернов, о которых вас обязательно спросят.

API Gateway

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

Он располагается на границе системы или сети микросервисов и служит единой точкой входа для всех клиентских запросов.

На собеседовании вас как правило будут спрашивать следующие вещи, давайте разберем потенциальные вопросы подробнее.

Как происходит маршрутизация и балансировка нагрузки?

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

Как API Gateway решает задачу взаимодействия различных протоколов?

На этот вопрос можно ответить, что API Gateway может выполнять трансляцию различных протоколов и форматов данных, например, преобразовывать HTTP-запросы в формат, который используется другими сервисами, такими как gRPC, для совместимости между микросервисами с разными протоколами.

Что такое трансформация запросов?

Ваш ответ должен заключаться в том, что API Gateway предоставляет механизмы для изменения запросов и ответов в процессе их передачи между клиентами и сервисами. Например, изменение параметров запросов, преобразование тела запроса или тела ответа, изменение заголовков и т.д.

Кэширование

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

Что такое агрегация и как она работает?

Агрегация позволяет объединить несколько API в один интерфейс, упрощая клиентскую интеграцию. Например, вместо обращения к каждому API отдельно, клиент может сделать один запрос к агрегированному API, который самостоятельно взаимодействует с несколькими сервисами.

Например, во многих e-commerce платформах API агрегирует функциональности различных сервисов (каталог продуктов, платежи, доставка, аутентификация пользователей) в один интерфейс.

Что такое офлоадинг Gateway?

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

Service Discovery

Следующий паттерн, который мы рассмотрим это Service Discovery.

Представим себе несколько микросервисов, которые составляют одно более-менее сложное приложение. Разумеется, они будут каким-то образом взаимодействовать друг с другом. Обычно приложения на микросервисах работает через виртуализацию или в контейнерах, количество инстансов сервиса и их местоположение могут динамически изменяться. При этом нам необходимо знать имена этих инстансов и где они находятся, чтобы запросы могли поступать к целевому микросервису. Здесь и вступает в игру Service Discovery – механизм, который позволяет нам определить местоположение каждого инстанса, который действует как реестр адресов.

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

Как работает Service Discovery?

Процесс работы данного механизма состоит из двух основных этапов – регистрация сервиса (когда сервис сообщает реестру, что он доступен) и поиск сервиса (когда клиент запрашивает у реестра информацию о местоположении нужного сервиса).

У данного паттерна есть несколько типов, первый из которых это Client-Side Discovery.

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

Второй вариант реализации это Server-Side Discovery.

Здесь клиент отправляет запросы на общий адрес (например через балансировщик нагрузки), а далее сервер сам запрашивает у реестра список доступных инстансов и направляет запрос к одному из них. Из недостатков здесь разве что необходимость настройки и управления балансировщиком нагрузки.

Что еще могут спросить на собеседовании? Разумеется, о подходах к регистрации.

Существует два основных подхода, это Self-Registration и Third-Party Registration.

В первом варианте сервис сам регистрирует себя в реестре и периодически отправляет сообщения типа «Я живой!» для подтверждения того, что он активен. Модель самостоятельной регистрации имеет ряд преимуществ и недостатков. Одно из преимуществ заключается в её относительной простоте и отсутствии необходимости в посредничестве других компонентов системы. Однако существенным недостатком является привязка экземпляров сервисов к реестру сервисов, что означает необходимость реализации кода регистрации на каждом используемом языке и фреймворке.

Альтернативным подходом, который отделяет услуги от Реестра услуг, является схема Third-Party Registration.

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

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

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

Circuit Breaker

Circuit Breaker это еще один полезный паттерн, который предназначен для обнаружения сбоев и управления ими. Он отслеживает взаимодействие между микросервисами и временно приостанавливает запросы к неисправному сервису, давая ему время на восстановление. Это помогает избежать дополнительной нагрузки на неисправный сервис и предотвращает эффект домино.

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

Собственно, его можно сравнить с обычным автоматом в вашем доме (автоматическим выключателем), который разорвет соединение, если что-то пойдет не так. «Выключатель» может работать в трех состояниях, закрытом, открытом и частично открытом. Каждое состояние имеет чётко определённое назначение и правила перехода.

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

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

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

Event-Driven Architecture

Архитектура, управляемая событиями, она же EDA, она же Event-Driven Architecture. Это парадигма проектирования программного обеспечения, в которой компоненты системы взаимодействуют, генерируя события и реагируя на них. Такими событиями могут быть как действия пользователя, так и изменения состояния системы.

Когда вас спросят о ней на собеседовании, важно объяснить основные принципы этого подхода.

В EDA есть три типа компонентов:

  1. События – сообщения, которые информируют о произошедших изменениях или состояниях в системе.

  2. Поставщики событий – компоненты, которые генерируют события.

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

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

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

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

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

Будет здорово, если на собеседовании вы…

Упомянете технологии, которые часто используются с EDA.

Apache Kafka — популярный брокер сообщений для высокоскоростной обработки событий.

RabbitMQ — система управления сообщениями для асинхронных взаимодействий.

AWS SNS/SQS — сервисы для обработки событий и сообщений в облаке.

Apache Pulsar — распределенная система публикации и подписки на события.

Event Sourcing

В традиционных системах, при обновлении сущности, старые данные в БД перезаписываются, однако в микросервисной архитектуре такой подход приводит к несогласованности данных между несколькими сервисами, отсутствию возможности получить историю изменений данных и сложностям с отладкой.

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

Здесь каждое событие представляет собой изменение состояния (например, OrderCreated, OrderUpdated, OrderCancelled), текущее состояние определяется путем воспроизведения всех прошлых событий, что позволяет отслеживать полную историю и легко восстанавливать предыдущие состояния.

Strangler Pattern

Паттерн с говорящим названием, если переводить с английского, «удушитель» :)

Этот подход применяется при переходе от монолита к микросервисам и свое название он получил от того, как лиана медленно душит дерево, постепенно вытесняя его рост. Аналогично, Strangler Pattern предполагает постепенную замену частей монолитного приложения микросервисами.

В основном его используют, когда полное переписывание системы несет значительные риски, особенно когда система устаревшая, со сложной кодовой базой, рефакторинг которой осуществить невозможно. У него также есть несколько существенных особенностей. Вначале, мы осуществляем постепенную миграцию, заменяя отдельные функции или модули. Далее они сосуществуют с монолитом, обеспечивая бесперебойную работу системы и в конечном итоге, происходит «удушение», то есть окончательная замена монолита.

Если на собеседовании вас спросят, какие компоненты стоит отключить или реорганизовать в первую очередь…

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

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

Database per Service

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

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

Однако, использование паттерна Database per Service также влечет за собой ряд проблем. Одной из ключевых проблем является обеспечение консистентности данных между микросервисами. Поскольку каждый микросервис имеет свою собственную базу данных, поддержание согласованности данных, особенно при их изменении в разных сервисах, может стать сложной задачей. Это особенно актуально при наличии транзакций, охватывающих несколько сервисов, что нарушает принципы ACID в традиционных реляционных СУБД.

Для решения этой проблемы часто используют различные подходы, например событийную модель Event-Driven Architecture, которую я уже рассмотрела выше.

Кроме того, для обработки распределённых транзакций, когда необходимо изменить данные в нескольких микросервисах, можно использовать паттерн Saga. Он представляет собой серию локальных транзакций, выполняемых разными сервисами, каждая из которых имеет свою логику отката, если одна из транзакций не удалась.

CQRS (Command Query Responsibility Segregation)

CQRS это паттерн, используемый в разработке для разделения задач обработки команд и выполнения запросов к данным.

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

Основные принципы, на которые опирается этот паттерн, следующие: команды должны отвечать только за изменения в системе (создание, обновление, удаление данных), а запросы только за чтение данных; поскольку команды и запросы часто имеют разные характеристики производительности, микросервисы можно масштабировать независимо. Например, микросервис, обрабатывающий часто изменяющиеся данные (команды), можно масштабировать отдельно от сервиса, который работает с большими объёмами запросов на чтение; Event-Driven Architecture, которую мы уже рассмотрели выше, дополняет CQRS, позволяя микросервисам обмениваться сообщениями и поддерживать консистентность данных в распределённых системах. События могут уведомлять другие микросервисы об изменениях состояния, произошедших в результате выполнения команд.

На собеседовании может встать вопрос, когда следует использовать CQRS?

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

Как подготовиться к собеседованию

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

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

Следует также быть готовым к ряду практических вопросов о реализации данных паттернов, о том какие инструменты и библиотеки вы могли бы использовать и рассуждать о типичных проблемах с микросервисной архитектурой. Как вы разделите операции чтения и записи для повышения производительности с помощью CQRS? Как вы можете обеспечить отказоустойчивость в распределенной системе с использованием Circuit Breaker? Как реализовать Service Discovery для динамически изменяющихся сервисов в облаке?

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


Паттерны микросервисной архитектуры

OTUS совместно с экспертами платформы algocode проведёт открытый онлайн-урок, посвящённый паттернам микросервисной архитектуры.

Дата и время: 28 октября 2025 года, 19:00 (МСК)
Формат: онлайн
Спикер: Даниил — Team Lead в крупной технологической компании, имеет более 5 лет опыта и провёл свыше 50 технических собеседований.

Темы урока:

  • основные паттерны микросервисной архитектуры: service discovery, retries, API Composition и другие;

  • практические примеры применения паттернов (bulkhead для кеширования, CQRS с MongoDB и Kafka, rate limiter при масштабировании);

  • проектирование надёжной архитектуры и подготовка систем к высоким нагрузкам;
    взаимодействие между сервисами и работа с брокерами сообщений;

  • распространённые ошибки при проектировании микросервисов;

  • методы отслеживания и анализа проблем в системе.

    Подробности доступны на странице урока.

Справочная информация:
Материалы algocode включены в программу курса OTUS «Golang Developer. Professional» и нацелены на освоение паттернов микросервисной архитектуры.

Комментарии (0)