← Назад к вопросам
Как работает синхронная коммуникация?
2.0 Middle🔥 151 комментариев
#REST API и микросервисы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Синхронная коммуникация в распределённых системах
Синхронная коммуникация — это запрос-ответ модель, где отправитель ждёт ответа перед продолжением работы. Она противоположна асинхронной коммуникации.
Основной принцип
Клиент (Service A) Сервер (Service B)
│ │
├─── HTTP Request ────────────>│
│ │
│ Обработка
│ │
│<─── HTTP Response ──────────┤
│ │
Продолжение работы
Клиент блокируется и ждёт ответа. Это простая и понятная модель, но имеет недостатки.
Синхронные протоколы
1. HTTP / REST API
Самый распространённый протокол для синхронной коммуникации:
// Service A: вызов Service B
@Service
public class OrderService {
private final RestTemplate restTemplate;
public Order createOrder(OrderRequest request) {
// 1. Отправляем запрос на payment-service
PaymentResponse payment = restTemplate.postForObject(
"http://payment-service/process",
new PaymentRequest(request.getAmount()),
PaymentResponse.class
); // БЛОКИРУЕМСЯ здесь и ждём ответа
// 2. Продолжаем только когда получили ответ
if (payment.isSuccessful()) {
Order order = new Order(request);
order.setPaymentId(payment.getId());
return orderRepository.save(order);
}
throw new PaymentException();
}
}
Основные характеристики:
- Запрос-ответ: GET, POST, PUT, DELETE
- Блокирующая: клиент ждёт
- Гарантии: ACID, транзакции
- Сложность: простая
2. RPC (Remote Procedure Call)
Вызов удалённого метода как локального:
// gRPC - типизированный RPC
// service.proto
service PaymentService {
rpc ProcessPayment(PaymentRequest) returns (PaymentResponse);
}
// Java клиент
public class OrderService {
private final PaymentServiceGrpc.PaymentServiceBlockingStub paymentStub;
public Order createOrder(OrderRequest request) {
// Вызов гарантированно вернёт ответ
PaymentResponse response = paymentStub.processPayment(
PaymentRequest.newBuilder()
.setAmount(request.getAmount())
.build()
); // БЛОКИРУЕТСЯ
return createOrderWithPayment(response);
}
}
Как работает синхронная коммуникация (техничное объяснение)
На уровне потоков (Threads)
Main Thread (Клиент)
│
├─ Создаёт HTTP клиент
│
├─ Отправляет запрос
│
├─ ЖДЁТ (thread.wait()) ────────┐
│ │
│ Сервер обрабатывает запрос │
│ Отправляет ответ │
│ │
│<────────────────────────────────┤
│
├─ Пробуждается (thread.notify())
│
└─ Обрабатывает ответ
С помощью Spring WebClient
@Service
public class OrderService {
private final WebClient webClient;
public Order createOrder(OrderRequest request) {
// Синхронный блокирующий вызов
PaymentResponse payment = webClient.post()
.uri("http://payment-service/process")
.bodyValue(new PaymentRequest(request.getAmount()))
.retrieve()
.bodyToMono(PaymentResponse.class)
.block(); // БЛОКИРУЕМСЯ до получения ответа
return createOrderWithPayment(payment);
}
}
Полный цикл синхронного запроса
1. CLIENT SIDE
├─ Создание request object
├─ Сериализация (JSON → HTTP body)
├─ Отправка HTTP запроса
└─ ОЖИДАНИЕ ответа (блокировка потока)
2. NETWORK
├─ TCP handshake (3-way)
├─ TLS handshake (если HTTPS)
├─ Отправка HTTP headers
├─ Отправка request body
└─ Получение response
3. SERVER SIDE
├─ Получение запроса
├─ Парсинг HTTP
├─ Десериализация JSON
├─ Обработка бизнес-логики
├─ Сериализация ответа
└─ Отправка HTTP response
4. CLIENT SIDE (продолжение)
├─ Получение ответа
├─ Парсинг HTTP headers
├─ Десериализация JSON response
├─ Пробуждение потока
└─ Продолжение работы
Практический пример: Цепочка синхронных вызовов
@Service
public class CheckoutService {
private final OrderService orderService;
private final PaymentService paymentService;
private final InventoryService inventoryService;
private final NotificationService notificationService;
public void checkout(CheckoutRequest request) {
// 1. Проверка инвентаря (БЛОКИРУЕТСЯ)
InventoryResponse inventory = inventoryService.checkStock(request.getProductId());
if (!inventory.isAvailable()) throw new OutOfStockException();
// 2. Создание заказа (БЛОКИРУЕТСЯ)
Order order = orderService.createOrder(request);
// 3. Обработка платежа (БЛОКИРУЕТСЯ)
PaymentResponse payment = paymentService.processPayment(
order.getId(),
request.getAmount()
);
// 4. Отправка уведомления (БЛОКИРУЕТСЯ)
notificationService.sendEmail(order.getCustomerEmail(), order);
// 5. Финально завершаем заказ
order.setStatus("COMPLETED");
orderService.update(order);
}
}
// Временная шкала:
// 0-100ms : checkStock (HTTP)
// 100-200ms : createOrder (HTTP)
// 200-500ms : processPayment (HTTP)
// 500-600ms : sendEmail (HTTP)
// Итого: 600ms
Преимущества синхронной коммуникации
✓ Простота: запрос-ответ, просто как вызов метода
✓ Гарантии: ACID транзакции, консистентность
✓ Отладка: легко отследить flow, ошибки сразу видны
✓ Состояние: всегда знаешь текущее состояние
✓ Порядок: гарантирован порядок операций
Недостатки синхронной коммуникации
✗ Блокировка: если Service B медленен, весь flow замедляется
✗ Масштабируемость: много потоков = много памяти
✗ Сложность при сбое: если Service B не отвечает, timeout
✗ Связанность: Service A зависит от Service B
✗ Распределённые транзакции: сложно достичь ACID
Синхронный вызов с таймаутом
@Service
public class ResilientOrderService {
private final RestTemplate restTemplate;
public Order createOrder(OrderRequest request) {
try {
// Таймаут 5 секунд
PaymentResponse payment = restTemplate.exchange(
"http://payment-service/process",
HttpMethod.POST,
new HttpEntity<>(new PaymentRequest(request.getAmount())),
PaymentResponse.class
).getBody();
return createOrderWithPayment(payment);
} catch (ResourceAccessException e) {
// Timeout или сетевая ошибка
logger.error("Payment service timeout", e);
throw new PaymentUnavailableException();
}
}
}
Синхронная vs Асинхронная
| Критерий | Синхронная | Асинхронная |
|---|---|---|
| Модель | Запрос-Ответ | Опубликовать-Подписаться |
| Ожидание | Блокирует | Не блокирует |
| Гарантии | ACID | Eventual consistency |
| Задержка | 10ms - 5s | Может быть большой |
| Scalability | Низкая (потоки) | Высокая (event queue) |
| Сложность | Простая | Сложнее |
| Пример | REST API | Kafka, RabbitMQ |
| Когда использовать | Критичные операции | Некритичные события |
Когда использовать синхронную коммуникацию
// ✓ Финансовые транзакции (платежи)
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
// ОБЯЗАТЕЛЬНО синхронно, нельзя потерять платёж
paymentService.processPayment(request); // БЛОКИРУЕМ
return ResponseEntity.ok(orderRepository.save(order));
}
// ✓ Аутентификация
@GetMapping("/profile")
public ResponseEntity<UserProfile> getProfile(@AuthenticationPrincipal User user) {
// ОБЯЗАТЕЛЬНО синхронно, нужно сразу знать результат
User validUser = authService.validateToken(user); // БЛОКИРУЕМ
return ResponseEntity.ok(userProfileService.build(validUser));
}
// ✗ Отправка писем
@PostMapping("/notify")
public ResponseEntity<Void> sendNotification(@RequestBody NotifyRequest request) {
// ПЛОХО синхронно - можно использовать очередь
emailService.send(request); // Это может занять 5 секунд!
return ResponseEntity.ok().build();
}
Вывод
Синхронная коммуникация — это базовый механизм взаимодействия в HTTP мире:
- Просто: вызвал метод, получил ответ
- Надёжно: знаешь результат сразу
- Но медленно: в высоконагруженных системах нужна асинхронность
Практический совет: используй синхронные вызовы для критичных операций, асинхронные для остального (события, уведомления, аналитика).