У чому різниця між синхронною та асинхронною комунікацією
Головна відмінність між синхронною та асинхронною взаємодією полягає в тому, чи змушений відправник чекати на відповідь від одержувача, щоб продовжити свою роботу.
🟢 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 зберігають повідомлення на диск до відновлення роботи сервісу).