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

«Как устроена микросервисная архитектура?» — вопрос из категории Архитектура, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Микросервисная архитектура (MSA) — это стиль построения приложения как набора слабо связанных, независимо развертываемых сервисов, каждый из которых реализует конкретную бизнес-возможность (bounded context) и общается по сети через легковесные протоколы.

Ключевые характеристики:

  • Организация вокруг бизнес-возможностей: Сервисы выравниваются по доменным областям (например, Order Service, Payment Service, User Service), а не по техническим слоям (UI, бизнес-логика, БД).
  • Децентрализованное управление данными: Каждый сервис владеет своей моделью и БД. Нет единой, общей для всех схемы БД. Для обмена данными используется API.
  • Независимое развертывание: Сервисы можно разрабатывать, тестировать и выкатывать независимо друг от друга, что ускоряет циклы доставки.
  • Явное определение и версионирование API: Контракты между сервисами четко определены (часто через OpenAPI/Swagger) и версионируются для обеспечения обратной совместимости.
  • Отказоустойчивость: Система проектируется с учетом отказов отдельных сервисов (принцип «Design for Failure»).

Типичный стек технологий:

  • Коммуникация: REST/HTTP, gRPC (для внутреннего RPC), асинхронные сообщения (Apache Kafka, RabbitMQ).
  • Обнаружение сервисов: Consul, Eureka, etcd.
  • Конфигурация: Spring Cloud Config, Consul KV, внешние хранилища (S3).
  • Оркестрация: Kubernetes, Docker Swarm для управления жизненным циклом контейнеров.
  • Мониторинг и трассировка: Prometheus + Grafana для метрик, Jaeger/Zipkin для распределенной трассировки, ELK-стек для логов.

Пример взаимодействия:

// Order Service (Java/Spring) вызывает Inventory Service через REST
@RestController
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;

    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest request) {
        // 1. Проверить наличие товара через Inventory Service
        ResponseEntity<InventoryResponse> response = restTemplate.postForEntity(
            "http://inventory-service/api/items/check",
            request.getItems(),
            InventoryResponse.class
        );
        if (!response.getBody().isAvailable()) {
            throw new InsufficientStockException();
        }
        // 2. Сохранить заказ в своей БД
        return orderService.save(request);
        // 3. Асинхронно отправить событие о новом заказе в Kafka для Payment Service
    }
}

Сложности (и как с ними борюсь):

  1. Распределенные транзакции: Использую паттерн Saga (компенсирующие транзакции) и идемпотентность операций.
  2. Сложность отладки: Внедряю сквозные идентификаторы запросов (correlation ID) и использую Jaeger для визуализации полного пути запроса.
  3. Управление конфигурацией: Храню конфиги в Git с шифрованием секретов и обновляю через Config Server, который подтягивает изменения в рантайме.
  4. Тестирование: Пишу интеграционные тесты с Testcontainers и контрактные тесты (Pact) для проверки взаимодействия между сервисами.