Синхронные и асинхронные интеграции, форматы обмена
Синхронно или через очередь — выбор, который аналитик обосновывает на каждой второй интеграции, и вопрос, который любят задавать на позицию middle+.
Синхронная интеграция — вызывающая сторона ждёт ответа и не может продолжить без него. Асинхронная — отправитель кладёт сообщение и продолжает работу, а получатель обработает его позже.
Вопрос 1. Когда вы выберете очередь, а когда синхронный вызов?
Что проверяют. Умение обосновать выбор компромиссами, а не модой. Ответ «микросервисы же, значит Kafka» — провал.
| Критерий | Синхронно (REST/gRPC) | Асинхронно (очередь/событие) |
| Нужен ответ прямо сейчас | да: проверка баланса, авторизация платежа | нет: начисление бонусов, отправка письма |
| Пиковые нагрузки | перегружают получателя | очередь сглаживает пик |
| Получатель временно недоступен | операция падает | сообщение подождёт в очереди |
| Сложность отладки | ниже: видно вызов и ответ | выше: нужны корреляционные идентификаторы |
| Согласованность | сразу | в конечном счёте (eventual) |
| Связанность систем | высокая | низкая: издатель не знает подписчиков |
Правило, которое хорошо звучит на собеседовании: синхронно — то, без чего пользователь не может продолжить прямо сейчас; асинхронно — всё остальное, особенно то, что порождает эффекты в других системах. Оформление заказа: проверка оплаты — синхронно, уведомление клиента, начисление бонусов, резерв на складе и передача в аналитику — событиями.
Вопрос 2. Что нужно описать в требованиях к асинхронной интеграции?
Что проверяют. Знаете ли вы, что очередь приносит собственный набор проблем, и умеете ли вы заранее закрыть их требованиями.
- Гарантия доставки. Почти все брокеры дают at-least-once: сообщение может прийти дважды. Значит, обработчик обязан быть идемпотентным — по идентификатору события. «Ровно один раз» на практике достигается именно так, а не магией брокера.
- Порядок. Гарантируется обычно только внутри партиции или очереди с одним ключом. Если важен порядок событий по заказу — ключом партиционирования делают идентификатор заказа.
- Повторы и dead letter queue. Сколько раз пробуем, с какой задержкой, куда складываем неразобранное и кто это разбирает.
- Схема сообщения и её эволюция. Контракт события — такой же контракт, как API.
- Срок хранения и объём. Сколько живут сообщения, что происходит при переполнении.
- Наблюдаемость. Сквозной correlationId через все сервисы, иначе разобрать инцидент невозможно.
Контракт события удобно описывать так же строго, как REST-метод:
{
"eventId": "8f2a41d0-6b7e-4f10-9a37-1b1c5f8e2ab9",
"eventType": "order.created",
"version": 1,
"occurredAt": "2026-03-01T10:15:32Z",
"correlationId": "req-2f9c11a4",
"producer": "order-service",
"payload": {
"orderId": 1024,
"customerId": 7,
"total": 3650.00,
"currency": "RUB",
"items": [
{ "productId": 15, "qty": 2, "price": 450.00 },
{ "productId": 42, "qty": 1, "price": 2750.00 }
]
}
}
Поля eventId (для дедупликации у потребителя), version (для эволюции схемы) и correlationId (для сквозной трассировки) — то, что забывают чаще всего, а спрашивают почти всегда.
Вопрос 3. JSON, XML или protobuf — как выбрать формат обмена?
Что проверяют. Знаете ли вы сильные стороны каждого, а не только тот, с которым работали.
| Формат | Сильные стороны | Где применяют |
| JSON | читаем, прост, поддержан везде, схема через JSON Schema или OpenAPI | публичные и внутренние REST API, события |
| XML | строгая схема XSD, пространства имён, подпись документа, вложенные структуры и атрибуты | госсистемы, банки, SOAP, обмен документами, ЭДО |
| protobuf | бинарный, компактный, схема обязательна, кодогенерация, быстрый | gRPC между сервисами, высоконагруженные потоки |
Пример одного и того же заказа в XML со схемой — типичный вид интеграции с регулируемой системой:
<?xml version="1.0" encoding="UTF-8"?>
<Order xmlns="urn:shop:order:1.0" id="1024">
<Customer id="7">Анна Петрова</Customer>
<CreatedAt>2026-03-01T10:15:32Z</CreatedAt>
<Items>
<Item productId="15" qty="2" price="450.00"/>
<Item productId="42" qty="1" price="2750.00"/>
</Items>
<Total currency="RUB">3650.00</Total>
</Order>
И тот же контракт в виде схемы protobuf — обратите внимание, что номера полей часть контракта: их нельзя переиспользовать после удаления поля.
syntax = "proto3";
message Order {
int64 id = 1;
int64 customer_id = 2;
string created_at = 3;
repeated Item items = 4;
string currency = 5;
double total = 6;
}
message Item {
int64 product_id = 1;
int32 qty = 2;
double price = 3;
}
Практический ответ: JSON по умолчанию; XML — когда так требует внешняя регулируемая сторона или нужна подпись документа; protobuf — когда критичны объём трафика и скорость и обе стороны внутренние.
Типичные ошибки кандидатов
- Выбирают очередь «для развязки», не задумываясь, что пользователю всё равно нужен ответ здесь и сейчас.
- Считают, что брокер гарантирует ровно одну доставку, и не требуют идемпотентности потребителя.
- Не описывают retry-политику и dead letter queue — потерянные сообщения обнаруживаются в проде.
- Полагаются на глобальный порядок сообщений, которого у брокера нет.
- Забывают про версию схемы события и потом ломают всех потребителей одним полем.
- Считают XML «устаревшим»: в госинтеграциях и ЭДО он стандарт де-факто, и подпись документа проще сделать именно в нём.
Как ответить кратко
Синхронно делаю то, без чего пользователь не может продолжить: авторизация платежа, проверка остатка. Всё остальное — событиями: уведомления, бонусы, аналитика, потому что очередь сглаживает пики и переживает недоступность получателя, ценой отложенной согласованности и более сложной отладки. Для асинхронной интеграции обязательно фиксирую в требованиях: доставка at-least-once, поэтому потребитель идемпотентен по eventId; порядок гарантируется только по ключу партиционирования; политика повторов и dead letter queue; версия схемы события и сквозной correlationId. Формат по умолчанию JSON, XML — если так требует внешняя регулируемая сторона или нужна подпись, protobuf — для внутренних высоконагруженных вызовов.