← Назад к вопросам

Как работает синхронная коммуникация?

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 Асинхронная

КритерийСинхроннаяАсинхронная
МодельЗапрос-ОтветОпубликовать-Подписаться
ОжиданиеБлокируетНе блокирует
ГарантииACIDEventual consistency
Задержка10ms - 5sМожет быть большой
ScalabilityНизкая (потоки)Высокая (event queue)
СложностьПростаяСложнее
ПримерREST APIKafka, 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 мире:

  • Просто: вызвал метод, получил ответ
  • Надёжно: знаешь результат сразу
  • Но медленно: в высоконагруженных системах нужна асинхронность

Практический совет: используй синхронные вызовы для критичных операций, асинхронные для остального (события, уведомления, аналитика).