В чём разница между синхронной и асинхронной коммуникацией
Главное различие между синхронным и асинхронным взаимодействием заключается в том, вынужден ли отправитель ждать ответа от получателя, чтобы продолжить свою работу.
🟢 Junior Level
Главное различие между синхронным и асинхронным взаимодействием заключается в том, вынужден ли отправитель ждать ответа от получателя, чтобы продолжить свою работу.
Синхронная (Телефонный звонок):
[Сервис A] ──Запрос (HTTP/gRPC)──► [Сервис B]
│ (Поток Сервиса A заблокирован / ждёт ответа)
◄──────────Ответ (200 OK)────────┘
Асинхронная (Отправка письма / Email):
[Сервис A] ──Публикация события──► [Брокер: Kafka / RabbitMQ]
│ (Сразу продолжает работу!) │
▼ (Обработка позже)
[Сервис B]
Основные понятия:
- Синхронная коммуникация (Point-to-Point): Клиент отправляет запрос (REST, gRPC) и блокирует поток исполнения до получения ответа (или истечения таймаута). Если целевой сервис недоступен, запрос немедленно завершается ошибкой.
- Асинхронная коммуникация (Message-Driven / Event-Driven): Отправитель публикует сообщение в промежуточный буфер (брокер сообщений) и сразу возвращает управление. Получатель вычитывает сообщение из очереди тогда, когда у него появятся свободные ресурсы.
🟡 Middle Level
1. Детальное сравнительное сопоставление
| Критерий | Синхронная коммуникация (REST, gRPC) | Асинхронная коммуникация (Kafka, RabbitMQ) |
|---|---|---|
| Временная связанность (Temporal Coupling) | Высокая: Оба сервиса обязаны быть онлайн одновременно в момент вызова. | Нулевая: Сервис-получатель может быть выключен; он обработает сообщения после запуска. |
| Пространственная связанность (Spatial Coupling) | Высокая: Клиент обязан знать сетевой адрес, DNS или имя сервиса. | Низкая: Продюсер знает только имя топика; он ничего не знает о потребителях. |
| Модель согласованности | Немедленная консистентность (Immediate Consistency). | Согласованность в конечном счете (Eventual Consistency). |
| Сглаживание пиков нагрузки (Peak Smoothing) | Нет: всплеск входящего трафика передается напрямую на бэкенд и базу данных. | Да: Брокер буферизует миллионы сообщений на диске; консьюмер читает их в своем темпе. |
| Сложность отладки и трассировки | Низкая: стек-трейс и логи линейны; легко тестировать в Postman. | Высокая: распределённые события, гонки сообщений, дубликаты, необходимость DLQ. |
| Каскадные сбои (Cascading Failures) | Высокий риск: зависание одного сервиса блокирует вызывающие сервисы. | Низкий риск: отказ консьюмера изолирован внутри очереди. |
2. Примеры в коде
А. Синхронный вызов (Spring WebClient):
@Service
@RequiredArgsConstructor
public class InventoryClient {
private final WebClient webClient;
public Mono<InventoryResponse> checkStock(String productId) {
return webClient.get()
.uri("/inventory/{id}", productId)
.retrieve()
.bodyToMono(InventoryResponse.class)
.timeout(Duration.ofSeconds(2)); // Таймаут критически важен!
}
}
Б. Асинхронный вызов (Spring Kafka):
@Service
@RequiredArgsConstructor
public class NotificationSender {
private final KafkaTemplate<String, EmailNotificationEvent> kafkaTemplate;
public void queueEmail(EmailNotificationEvent event) {
// Отправили и забыли: поток метода не ждет отправки реального письма
kafkaTemplate.send("email-notifications", event.userId(), event);
}
}
🔴 Senior Level
1. Сглаживание пиковых нагрузок (Traffic Leveling / Backpressure)
Главное архитектурное преимущество асинхронных очередей — защита базы данных от разрушения:
Синхронный пик (Black Friday):
10 000 RPS ──► [Order Service] ──(10 000 одновременных транзакций)──► [PostgreSQL: СМЕРТЬ 💥]
Асинхронный пик с Kafka (Pull-модель):
10 000 RPS ──► [Kafka Topic: буферизация на диске]
│ (Backpressure: консьюмер берет строго по 100 сообщений)
▼
[Order Consumer] ──(100 TPS: стабильная нагрузка)──► [PostgreSQL: СТАБИЛЬНО ✅]
В синхронной архитектуре всплеск трафика пробивает все слои вплоть до диска СУБД. В асинхронной модели консьюмер работает по Pull-модели (max.poll.records = 100), физически не позволяя нагрузке превысить пропускную способность базы данных.
2. Обработка ошибок: HTTP Status Codes vs Dead Letter Queue (DLQ)
- В синхронных вызовах: Ошибка обрабатывается на месте: возврат HTTP
4xx/5xx, срабатывание@CircuitBreaker, повтор@Retryили передача управления в Fallback. - В асинхронных системах: Если сообщение не может быть обработано (например, невалидные данные):
- Нельзя просто бесконечно повторять чтение, так как это заблокирует смещение (offset) всей партиции.
- Нельзя просто выбросить сообщение, потеряв деньги клиента.
- Используется паттерн Retry Topics + Dead Letter Topic (DLT / DLQ):
[Main Topic] ──(Ошибка)──► [Retry Topic (Backoff 5s)] ──(Ошибка)──► [Dead Letter Topic (DLT)]
│
▼
[Manual Audit / Alert]
3. Гибридная парадигма: Async бэкенд с Sync пользовательским опытом
Бизнес часто требует немедленной обратной связи в UI (например, «Заказ успешно оформлен»), в то время как весь бэкенд спроектирован асинхронно:
- HTTP 202 Accepted: Шлюз быстро проверяет синтаксис, генерирует
orderId, кладет задачу в очередь и немедленно отдает клиенту ответ:HTTP 202 Acceptedсо ссылкой на статус/api/orders/{id}/status. - Push-уведомление в UI: Фронтенд слушает результат через WebSocket или Server-Sent Events (SSE). Когда фоновая цепочка саги завершается, сервер пушит событие
OrderConfirmedпрямо в браузер.
4 Tricky Questions
1. Почему отправка сообщения через KafkaTemplate.send() может неожиданно заблокировать вызывающий поток?
Ответ:
Метод kafkaTemplate.send() считается асинхронным, но он может заблокировать поток на время до max.block.ms (по умолчанию 60 секунд) в следующих случаях:
- Отсутствие метаданных кластера: При первом вызове продюсер должен получить метаданные о топике и лидерах партиций от брокера. Если сеть недоступна, поток блокируется в ожидании метаданных.
- Переполнение буфера памяти: Если сеть медленнее входящего потока данных, локальный буфер продюсера (
buffer.memory, по умолчанию 32 МБ) переполняется, и методsend()блокирует вызывающий поток. - Ошибочный вызов
.get()на возвращенном объектеCompletableFuture.
2. Что такое Poison Pill («Отравленная пилюля») в очереди сообщений и как от неё защититься?
Ответ:
Poison Pill — это поврежденное сообщение в топике, которое вызывает неустранимое исключение при попытке обработки (например, поврежденный JSON, который падает на этапе десериализации до попадания в бизнес-код).
Консьюмер падает $\to$ брокер перезапускает консьюмер $\to$ консьюмер снова читает то же самое сообщение $\to$ падает опять (бесконечный цикл сбоя партиции).
Защита: В Spring Kafka используется ErrorHandlingDeserializer. Он перехватывает ошибку десериализации, оборачивает её в специальный заголовок и передает управление в RecordListener, который сразу отправляет битое сообщение в Dead Letter Topic (DLT) без зависания консьюмера.
3. Как изменится latency пользовательского запроса при замене синхронного HTTP-вызова на асинхронную связку через брокер?
Ответ: Latency отдельного единичного запроса вырастет:
- В синхронном REST: клиент $\to$ сеть $\to$ сервер $\to$ ответ (единицы миллисекунд).
- В асинхронном флоу: клиент $\to$ запись в брокер $\to$ репликация на диск брокера $\to$ опрос консьюмером $\to$ обработка $\to$ нотификация клиента (десятки или сотни миллисекунд). Однако пропускная способность (Throughput) системы вырастет в разы, так как потоки не блокируются в ожидании, а нагрузка равномерно распределяется во времени.
4. Почему нельзя использовать асинхронную коммуникацию при прямых операциях списания средств в банковских POS-терминалах?
Ответ: Сценарий оплаты на кассе магазина требует строгой синхронности: кассовый терминал удерживает открытый сетевой сокет и требует бинарного ответа «Одобрено» или «Отклонено» в течение 2–3 секунд, пока покупатель держит карту у терминала. Асинхронная Eventual Consistency здесь неприменима: кассир не может отпустить покупателя с товаром, надеясь, что через 10 минут событие из Kafka обработается успешно и деньги реально спишутся со счета.
🎯 Шпаргалка для интервью
Главное:
- Синхронная (REST, gRPC): Телефонный звонок. Быстро для чтения (Read), строгая консистентность, но Temporal Coupling и высокий риск каскадных сбоев.
- Асинхронная (Kafka, RabbitMQ): Почтовое письмо. Высокая отказоустойчивость, развязка сервисов во времени, защита базы данных от пиков нагрузки (Traffic Smoothing / Backpressure), но требует Eventual Consistency.
- Архитектурное правило:
- Запросы на чтение (Queries) $\to$ чаще Синхронные.
- Команды на изменение (Commands) и события (Events) $\to$ Асинхронные.
- В асинхронных системах обязательны идемпотентность и обработка «отравленных сообщений» через Dead Letter Queue (DLQ).
Красные флаги (Чего говорить НЕЛЬЗЯ):
- ❌ «Асинхронное взаимодействие всегда быстрее синхронного» (Latency одного запроса в асинхронной схеме выше из-за брокера, но выше общий throughput).
- ❌ «В микросервисах синхронные вызовы запрещены» (Для простых запросов на чтение синхронный REST/gRPC — идеальный выбор).
- ❌ «Если упал сервис-получатель, асинхронное сообщение теряется» (Брокеры вроде Kafka сохраняют сообщения на диск до возобновления работы сервиса).