Всем привет, меня зовут Сергей Прощаев, и в этой статье я расскажу про то, как не запутаться в многообразии способов общения между микросервисами. Руковожу направлением Java-разработки в FinTech, и за годы работы с распределенными системами я видел много боли: кто-то пытался построить событийную архитектуру на синхронных HTTP-вызовах, а кто-то, наоборот, тащил тяжеловесные очереди туда, где достаточно было простого REST.
Кажется, что может быть проще: один сервис вызывает другой. Но именно здесь, на стыке процессов, рождаются самые коварные проблемы. В этой статье я хочу не просто перечислить варианты, а погрузить вас в контекст выбора: когда синхронная блокировка — это зло, а когда она — простое и понятное решение. И главное — разобрать лучшие практики, которые реально работают в продакшене.
От вызова метода к сетевому запросу: подводные камни
Мы так привыкли к внутрипроцессным вызовам. Вызвал метод — получил результат. Компилятор может даже встроить этот вызов для оптимизации, сделав его практически бесплатным. В мире микросервисов все иначе.
Когда я впервые начал проектировать систему, где один сервис активно дергал другой через REST, я долго не мог понять, почему все работает так медленно. Пока не посчитал. Обычная передача одного пакета в дата-центре занимает миллисекунды. Казалось бы, немного. Но если вы делаете это сотни раз в рамках одной пользовательской операции, задержка становится критичной.
И это только вершина айсберга. Сетевой вызов — это сериализация, отправка пакета, десериализация на стороне получателя. Размер полезной нагрузки внезапно начинает иметь значение. Сколько раз вы задумывались о размере структуры данных при вызове метода внутри одного процесса? Никогда. А тут приходится. Мы в одном проекте сократили время ответа API на 40%, просто убрав из JSON-ответа поля, которые клиент все равно не использовал.
Самые большие проблемы, с которыми я сталкивался, возникали, когда разработчики пытались скрыть факт сетевого вызова за абстракциями. «Это просто вызов метода», — говорили они. А потом получали простой системы из-за тайм-аутов, которые не были предусмотрены. Разработчик должен знать, что он делает сетевой вызов. Это фундаментальное правило, которое я вынес еще из книги Сэма Ньюмена.
Типы отказов: когда все идет не по плану
Если вы думаете, что главная проблема распределенной системы — это когда сервис недоступен, вы ошибаетесь. Там, где один процесс вызывает другой, появляется целый зоопарк отказов. Таненбаум и Стин в своей книге описывают пять типов, но я выделю для себя самые болезненные.
Пропуск при отказе — вы отправили запрос, а ответа нет. И самое неприятное: вы не знаете, дошло ли сообщение до адресата. Или оно дошло, а ответ потерялся? В одном из наших проектов мы мучились с этим несколько недель, пока не выяснили, что проблема была в балансировщике нагрузки, который иногда «забывал» вернуть ответ клиенту.
Сбой синхронизации — ответ пришел, но с опозданием. Или наоборот, раньше, чем мы ожидали. В мире распределенных систем понятие «времени» становится очень относительным.
Ошибка ответа — вы получили ответ, но он какой-то странный. Запросили сводку заказа, а в ответе нет половины полей. И что делать? Это не тайм-аут, но и успехом не назовешь.
И самое важное — многие из этих ошибок носят временный характер. HTTP-коды нам в помощь. 500-я серия говорит о проблемах на стороне сервера, которые могут быть временными. 404 Not Found — это ошибка запроса, повторять бессмысленно. А 503 Service Unavailable — это сигнал подождать и попробовать снова.
Как-то в одном проекте я наблюдал за разбором аварии, когда из-за неправильной обработки 503 сервис начал бесконечно ретраить запросы, создавая лавинную нагрузку на базу данных. Система легла за 10 минут. Урок выучили жесткий: всегда нужно понимать природу ошибки и действовать соответственно.
Синхронная блокировка: просто, но опасно
В синхронном блокирующем вызове есть что-то родное. Мы все начинали с этого: отправил запрос, жду ответа. И когда мы переходим от монолита к микросервисам, первое желание — сохранить эту ментальную модель. Это и есть главная ловушка.
В моем опыте синхронные вызовы отлично работают, когда вы имеете дело с простой архитектурой и небольшой глубиной вызовов. Пока у вас нет цепочек. Но как только появляется цепочка вроде «Обработчик заказов → Оплата → Обнаружение мошенничества → Покупатель», начинаются проблемы.

В этой схеме сбой в любом из четырех сервисов приводит к провалу всей операции. И это еще не все. Каждый сервис держит открытое сетевое соединение в ожидании ответа. Если таких цепочек много, вы быстро упираетесь в лимит открытых подключений.
В одном проекте, описанном в сети, одна команда пыталась масштабироваться на Black Friday, и внезапно система умерла. Причина оказалась проста: длинные цепочки синхронных вызовов исчерпали пул соединений в API Gateway. Мы потратили три дня, чтобы переделать один критический сценарий на асинхронный. Это было больно, но полезно.
Асинхронная неблокирующая связь: сложно, но мощно
Асинхронная связь — это когда отправитель не ждет ответа. И здесь есть три основных варианта, каждый со своими плюсами и минусами.
Общие данные
Самый простой и недооцененный способ. Один сервис пишет данные в общее хранилище, другой — читает. В моей практике это часто была файловая система: один сервис выгружал отчеты в S3, а другой подхватывал их для обработки.
Этот подход особенно ценен, когда вам нужно интегрироваться со старыми системами. Мейнфреймы не умеют в Kafka, но читать файлы умеют. Мы так выстроили интеграцию с legacy-системой, которая до сих пор работает и не требует дорогостоящих доработок.
Главный минус — задержка. Потребитель обычно узнает о новых данных через polling, а это не лучший вариант для real-time сценариев.
Запрос-ответ
В этом стиле микросервис отправляет запрос и ждет ответ, но без блокировки. Это идеально для длительных операций. Например, когда заказ должен быть собран на складе, упакован и отправлен. Весь процесс может занять часы или дни.

Обратите внимание: в отличие от синхронного вызова, здесь нет прямого соединения между отправителем и получателем. Ответ может вернуться к другому экземпляру сервиса, поэтому нужно где-то хранить состояние запроса. В нашем проекте мы сохраняли информацию о заказе в базу данных, чтобы любой экземпляр мог продолжить обработку после получения ответа.
Событийное взаимодействие
Это мой любимый стиль. Микросервис просто транслирует факт, что что-то произошло. Кто и как на это отреагирует — его не касается.
Но именно здесь кроется много нюансов. Главный вопрос: что класть в событие? Есть два подхода.
Минималистичный — только идентификатор. Событие говорит: «Создан пользователь с id 123». Потребитель, получив событие, идет в сервис пользователей и забирает остальные данные. Кажется логичным, но порождает проблему: если у вас пять потребителей, все они одновременно дернут сервис пользователей, создавая пиковую нагрузку.
Максималистичный — все, что может понадобиться. Я предпочитаю этот подход. Событие содержит email, имя и другую информацию, которая может быть нужна потребителям. Микросервису уведомлений больше не нужно ходить в сервис пользователей — он самодостаточен.

У этого подхода есть недостатки: размер события может быть большим, а также вы рискуете раскрыть лишние данные. Но для большинства задач он работает лучше.
Реальная история: когда асинхронность спасает
В одном из проектов, когда я только начинал работать над системой ценообразования для банка, мы столкнулись с классической проблемой. Нужно было обрабатывать рыночные события и переоценивать портфели. Мы выстроили архитектуру с очередью сообщений и пулом воркеров. Все работало отлично, пока не появилась ошибка, из-за которой воркеры падали.
И тут началось самое интересное. Когда воркер падал, таймаут блокировки запроса истекал, и сообщение возвращалось в очередь. Другой воркер подхватывал его — и тоже падал. Мы наблюдали классическую «катастрофическую отказоустойчивость», как назвал это Мартин Фаулер.
В итоге нам пришлось внедрить максимальное количество повторных попыток и «лазарет» для сообщений — очередь недоставленных сообщений, куда уходили проблемные запросы. Плюс UI для просмотра и повторной отправки. Это был болезненный урок: асинхронная связь требует продуманной обработки ошибок и мониторинга.
Как выбирать стиль взаимодействия
После десятков проектов я выработал для себя простой алгоритм.
Определите, нужен ли вам ответ. Если для дальнейшей обработки результат не нужен — сразу смотрите в сторону событий или асинхронного запроса-ответа.
Оцените время выполнения. Если операция занимает больше пары секунд — синхронный вызов не подходит. У вас просто не хватит времени удержания соединения.
Посмотрите на цепочку вызовов. Если у вас глубокая вложенность, замените синхронные вызовы в середине цепочки на асинхронные.
Учитывайте требования к связанности. Если вы хотите минимизировать зависимости между сервисами — событийное взаимодействие ваш выбор.
В моем проекте для финтеха мы пошли на смелый шаг: основной процесс обработки заказа мы оставили синхронным (быстро, просто, понятно), а все второстепенные сценарии — уведомления, аналитику, начисление бонусов — перевели на события. И это сработало. Система стала более отказоустойчивой, а разработка ускорилась.
Вместо заключения
Микросервисная архитектура не терпит догматизма. Нет единого правильного стиля взаимодействия. В одном проекте синхронные вызовы могут быть идеальным решением, в другом — привести к коллапсу. Главное — понимать компромиссы и уметь выбирать под конкретную задачу.

На курсе «Микросервисная архитектура» в OTUS мы подробно разбираем не только теорию, но и реальные кейсы. На бесплатном демо-уроке 30 марта в 20:00 «Стили взаимодействия микросервисов: 5 секретов, которые изменят ваш подход к backend-разработке» я покажу, как проектировать взаимодействие между микросервисами на примере реальной системы. Мы посмотрим на код, разберем ошибки и обсудим лучшие практики, которые реально работают в продакшене.
Если вы хотите научиться не просто вызывать один сервис из другого, а строить надежные, масштабируемые и слабосвязанные системы — приходите. Буду рад поделиться опытом. Записаться на урок.
Немного практики в тему — пройдите вступительный тест по микросервисной архитектуре и узнаете, есть ли пробелы в знаниях.
