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

Какие знаешь типы взаимодействия между микросервисами?

1.7 Middle🔥 201 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Типы взаимодействия между микросервисами

Микросервисная архитектура требует наличия механизмов взаимодействия между отдельными сервисами. Существует несколько подходов, каждый из которых имеет свои преимущества и недостатки.

1. Синхронное взаимодействие (Synchronous Communication)

1.1 REST (RESTful APIs)

HTTP запросы для прямого обращения между сервисами

// UserService обращается к OrderService
@Service
public class UserService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    public UserWithOrders getUser(Long userId) {
        // Получить пользователя
        User user = userRepository.findById(userId).orElseThrow();
        
        // Синхронный вызов к OrderService
        try {
            Order[] orders = restTemplate.getForObject(
                "http://order-service/api/users/" + userId + "/orders",
                Order[].class
            );
            user.setOrders(Arrays.asList(orders));
        } catch (RestClientException e) {
            // Order Service недоступен!
            logger.error("OrderService unavailable", e);
            // Что делать?
        }
        
        return user;
    }
}

Проблемы:

  • Прямая связанность — если OrderService падает, UserService не может ответить
  • Медленные ответы — общее время = время User + время Order
  • Network timeouts — нужно управлять таймаутами
  • Cascading failures — сбой одного сервиса вызывает сбой других

Решение — используй Circuit Breaker:

@Service
public class OrderServiceClient {
    
    @Autowired
    private RestTemplate restTemplate;
    
    @CircuitBreaker(name = "order-service")
    public Order[] getOrders(Long userId) {
        return restTemplate.getForObject(
            "http://order-service/api/users/" + userId + "/orders",
            Order[].class
        );
    }
    
    public Order[] getOrdersWithFallback(Long userId) {
        try {
            return getOrders(userId);
        } catch (Exception e) {
            // Fallback: вернуть пустой список или cached данные
            return new Order[0];
        }
    }
}

С Resilience4j:

resilience4j:
  circuitbreaker:
    configs:
      default:
        slidingWindowSize: 10
        minimumNumberOfCalls: 5
        failureRateThreshold: 50
        slowCallRateThreshold: 50
        slowCallDurationThreshold: 2000ms
    instances:
      order-service:
        baseConfig: default

1.2 gRPC

Более эффективная альтернатива REST с использованием Protocol Buffers

// user_service.proto
syntax = "proto3";

service UserService {
    rpc GetUser (GetUserRequest) returns (User);
    rpc GetOrders (GetUserRequest) returns (stream Order);
}

message GetUserRequest {
    int64 user_id = 1;
}

message User {
    int64 id = 1;
    string name = 2;
    string email = 3;
}

message Order {
    int64 id = 1;
    int64 user_id = 2;
    string status = 3;
}
// Сгенерированный код
public class UserServiceClient {
    
    private UserServiceGrpc.UserServiceBlockingStub stub;
    
    public UserServiceClient(Channel channel) {
        this.stub = UserServiceGrpc.newBlockingStub(channel);
    }
    
    public User getUser(long userId) {
        GetUserRequest request = GetUserRequest.newBuilder()
            .setUserId(userId)
            .build();
        return stub.getUser(request);
    }
    
    public List<Order> getOrders(long userId) {
        GetUserRequest request = GetUserRequest.newBuilder()
            .setUserId(userId)
            .build();
        
        List<Order> orders = new ArrayList<>();
        stub.getOrders(request).forEachRemaining(orders::add);
        return orders;
    }
}

Преимущества:

  • Очень быстрые (бинарный протокол)
  • Типизированные (Protocol Buffers)
  • Поддержка streaming
  • HTTP/2, multiplexing

Проблемы:

  • Более сложен в отладке
  • Не работает с браузерами (нужен грязный hack)
  • Требует кодогенерации

2. Асинхронное взаимодействие (Asynchronous Communication)

2.1 Message Queue (очереди сообщений)

Сервисы отправляют сообщения, другие их обрабатывают

// OrderService создаёт заказ и публикует событие
@Service
public class OrderService {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public Order createOrder(CreateOrderRequest request) {
        Order order = new Order(request);
        orderRepository.save(order);
        
        // Опубликовать событие
        rabbitTemplate.convertAndSend(
            "orders.exchange",
            "order.created",
            new OrderCreatedEvent(
                order.getId(),
                order.getUserId(),
                order.getTotal()
            )
        );
        
        return order;
    }
}

// NotificationService слушает события
@Service
public class NotificationService {
    
    @RabbitListener(
        bindings = @QueueBinding(
            value = @Queue("notification-queue"),
            exchange = @Exchange("orders.exchange"),
            key = "order.created"
        )
    )
    public void handleOrderCreated(OrderCreatedEvent event) {
        // Отправить email пользователю
        emailService.sendOrderConfirmation(event.getUserId());
    }
}

С Apache Kafka:

// Публикатор
@Service
public class OrderProducer {
    
    @Autowired
    private KafkaTemplate<String, OrderEvent> kafkaTemplate;
    
    public void publishOrderCreated(Order order) {
        kafkaTemplate.send(
            "orders-topic",
            String.valueOf(order.getId()),
            new OrderEvent(
                "ORDER_CREATED",
                order.getId(),
                order.getUserId()
            )
        );
    }
}

// Подписчик
@Service
public class OrderConsumer {
    
    @KafkaListener(
        topics = "orders-topic",
        groupId = "notification-service"
    )
    public void consumeOrderEvent(OrderEvent event) {
        if ("ORDER_CREATED".equals(event.getEventType())) {
            notificationService.sendOrderConfirmation(event.getUserId());
        }
    }
}

Преимущества:

  • Слабая связанность (loose coupling)
  • Асинхронность → лучшая масштабируемость
  • Естественно обрабатывает временные сбои
  • Легко добавить новых подписчиков

Проблемы:

  • Сложнее отлаживать (асинхронные потоки)
  • Может быть задержка обработки
  • Нужна гарантия доставки (at-least-once, at-most-once)
  • Сложнее тестировать

2.2 Event Sourcing

Все изменения хранятся как события

// Event
public abstract class DomainEvent {
    private final long aggregateId;
    private final LocalDateTime timestamp;
}

public class OrderCreatedEvent extends DomainEvent {
    private long orderId;
    private long userId;
    private BigDecimal amount;
    private List<OrderItem> items;
}

public class PaymentProcessedEvent extends DomainEvent {
    private long orderId;
    private long paymentId;
    private LocalDateTime processedAt;
}

// Event Store
@Service
public class EventStore {
    
    @Autowired
    private EventRepository eventRepository;
    
    public void append(DomainEvent event) {
        eventRepository.save(event);
        // Опубликовать в message queue для других сервисов
        eventPublisher.publish(event);
    }
    
    public List<DomainEvent> getEvents(long aggregateId) {
        return eventRepository.findByAggregateId(aggregateId);
    }
}

// Rebuild состояния из событий
public class OrderAggregateRoot {
    
    private long orderId;
    private long userId;
    private BigDecimal total;
    private OrderStatus status;
    
    public static OrderAggregateRoot fromEvents(List<DomainEvent> events) {
        OrderAggregateRoot order = new OrderAggregateRoot();
        
        for (DomainEvent event : events) {
            if (event instanceof OrderCreatedEvent) {
                OrderCreatedEvent e = (OrderCreatedEvent) event;
                order.orderId = e.getOrderId();
                order.userId = e.getUserId();
                order.total = e.getAmount();
                order.status = OrderStatus.CREATED;
            } else if (event instanceof PaymentProcessedEvent) {
                order.status = OrderStatus.PAID;
            }
        }
        
        return order;
    }
}

Преимущества:

  • Полная история всех изменений
  • Debugging просто (воспроизведи все события)
  • Легко добавить нововый observer
  • CQRS готов

Проблемы:

  • Усложняет архитектуру
  • Память (хранить все события)
  • Версионирование событий
  • Snapshots нужны для производительности

3. Гибридные подходы

3.1 Request-Reply через Message Queue

Асинхронность, но с гарантией ответа

// Запросчик
@Service
public class UserService {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public Order getOrderDetails(long orderId) {
        // Создать уникальный correlation ID
        String correlationId = UUID.randomUUID().toString();
        
        // Создать reply queue
        Queue replyQueue = new Queue();
        String replyQueueName = replyQueue.getName();
        
        // Отправить запрос
        rabbitTemplate.convertAndSend("order-requests", 
            new OrderRequest(orderId),
            message -> {
                message.getMessageProperties().setCorrelationId(correlationId);
                message.getMessageProperties().setReplyTo(replyQueueName);
                return message;
            }
        );
        
        // Получить ответ (блокирующий вызов)
        Message response = (Message) rabbitTemplate.receiveAndConvert(
            replyQueueName,
            5000  // 5 секунд timeout
        );
        
        return (Order) response.getPayload();
    }
}

// Ответчик
@Service
public class OrderService {
    
    @RabbitListener(queues = "order-requests")
    public Order handleOrderRequest(
            OrderRequest request,
            @Header("amqp_replyTo") String replyTo,
            @Header("amqp_correlationId") String correlationId) {
        
        Order order = orderRepository.findById(request.getOrderId())
            .orElseThrow();
        
        // Ответить
        rabbitTemplate.convertAndSend(replyTo, 
            order,
            message -> {
                message.getMessageProperties().setCorrelationId(correlationId);
                return message;
            }
        );
        
        return order;
    }
}

3.2 Saga Pattern (Distributed Transactions)

Координация нескольких сервисов в одной транзакции

// Choreography-based Saga
@Service
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private EventPublisher eventPublisher;
    
    public Order createOrder(CreateOrderRequest request) {
        // Step 1: Создать заказ
        Order order = new Order(request);
        order.setStatus(OrderStatus.PENDING_PAYMENT);
        orderRepository.save(order);
        
        // Step 2: Опубликовать событие
        eventPublisher.publish(new OrderCreatedEvent(order));
        
        return order;
    }
}

// PaymentService слушает OrderCreatedEvent
@Service
public class PaymentService {
    
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        try {
            // Обработать платёж
            Payment payment = processPayment(
                event.getOrderId(),
                event.getAmount()
            );
            
            // Опубликовать успех
            eventPublisher.publish(new PaymentSuccessfulEvent(payment));
        } catch (PaymentException e) {
            // Опубликовать ошибку → откат
            eventPublisher.publish(new PaymentFailedEvent(event.getOrderId()));
        }
    }
}

// OrderService откатывает заказ
@Service
public class OrderService {
    
    @EventListener
    public void handlePaymentFailed(PaymentFailedEvent event) {
        // Откатить заказ
        Order order = orderRepository.findById(event.getOrderId())
            .orElseThrow();
        order.setStatus(OrderStatus.CANCELLED);
        orderRepository.save(order);
        
        // Опубликовать отмену
        eventPublisher.publish(new OrderCancelledEvent(order));
    }
}

Orchestration-based Saga:

// Centralized orchestrator
@Service
public class OrderSagaOrchestrator {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private PaymentService paymentService;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Transactional
    public Order executeOrderSaga(CreateOrderRequest request) {
        // Step 1
        Order order = orderService.createOrder(request);
        
        try {
            // Step 2
            Payment payment = paymentService.processPayment(
                order.getId(),
                order.getTotal()
            );
            order.setPaymentId(payment.getId());
            
            // Step 3
            inventoryService.reserveItems(order.getItems());
            
            // Success
            order.setStatus(OrderStatus.CONFIRMED);
            orderService.save(order);
            
        } catch (Exception e) {
            // Откат всех шагов
            if (order.getPaymentId() != null) {
                paymentService.refund(order.getPaymentId());
            }
            order.setStatus(OrderStatus.CANCELLED);
            orderService.save(order);
            throw e;
        }
        
        return order;
    }
}

Сравнение подходов

ТипКонсистентностьСкоростьСвязанностьСложностьКогда использовать
RESTСинхронСредняяВысокаяНизкаяПростые запросы
gRPCСинхронОчень высокаяВысокаяСредняяHigh-frequency
Message QueueАсинхронВысокаяНизкаяСредняяСобытия, масштабируемость
Event SourcingАсинхронСредняяНизкаяВысокаяАудит, сложная логика
SagaEventualВысокаяНизкаяВысокаяРаспределённые транзакции

Рекомендации

  1. Начни с REST для простых синхронных запросов
  2. Используй Message Queue для событий и слабой связанности
  3. Применяй Circuit Breaker для защиты от cascading failures
  4. Рассмотри Saga для сложных распределённых бизнес-процессов
  5. Event Sourcing только если нужна полная история
  6. Комбинируй подходы — REST для读取, очереди для письма