Перейти к содержимому
Mihail Leybuk-Galin — главная
Архитектура систем2 мин чтения

Масштабирование электронной коммерции: за пределами монолита.

Переход устаревшей платформы электронной коммерции, обрабатывающей миллионы транзакций ежедневно, на микросервисную архитектуру. Подробный разбор трудностей, стратегий и итогового успеха распределенной системы.

Mihail Leybuk-GalinСистемный архитектор

1. Переломный момент

Любое успешное монолитное приложение рано или поздно достигает переломного момента. Для нашего клиента — одного из ведущих ритейлеров одежды — этот момент наступил во время пиковых нагрузок в Чёрную пятницу 2022 года. Монолитное приложение на Ruby on Rails, которое исправно служило ему пять лет, не выдержало устойчивой нагрузки в 45,000 запросов в секунду.

Симптомы были типичными: исчерпание пула подключений к базе данных, каскадные тайм-ауты и развёртывания, занимавшие больше часа из-за огромного объёма кодовой базы. Стало ясно, что горизонтальное масштабирование всего приложения больше не было ни экономически оправданным, ни технически достаточным.

2. Декомпозиция монолита

Переход не был одномоментным переписыванием всей системы. Мы применили паттерн Strangler Fig, постепенно выделяя ограниченные контексты. Мы начали с сервиса 'Cart', поскольку ему требовалась высокая доступность, но при этом у него было относительно мало зависимостей от других компонентов.

Декомпозиция монолита

Целевое состояние архитектуры (после миграции)

Открыть полноразмерное изображение

Направив трафик через новый API Gateway, мы смогли динамически перенаправлять запросы к эндпоинтам /cart в новый микросервис на Go, сохранив маршрутизацию всего остального трафика в прежний монолит. Это позволило проверить новый сервис в рабочей среде на части пользователей до полного переключения.

3. Переход к событийно-ориентированным подходам

По мере выделения новых сервисов (Inventory, Order Fulfillment, User Profiles) синхронные REST-вызовы между ними стали создавать неприемлемые задержки и жёсткую связанность. Мы внедрили Apache Kafka в качестве центральной нервной системы нашей архитектуры.

order_handler.go

func (h *OrderHandler) ProcessCheckout(ctx context.Context, req CheckoutRequest) error {
    // 1. Validate request
    if err := h.validator.Validate(req); err != nil {
        return err
    }

    // 2. Create Order Aggregate in local DB (Pending state)
    order := NewOrder(req)
    if err := h.repo.Save(ctx, order); err != nil {
        return err
    }

    // 3. Emit OrderCreated event to Kafka topic
    event := events.OrderCreatedEvent{
        OrderID: order.ID,
        Items:   order.Items,
        Total:   order.Total,
    }
    
    if err := h.eventBus.Publish(ctx, "orders.created", event); err != nil {
        // Compensating transaction or DLQ logic here
        return fmt.Errorf("failed to publish event: %w", err)
    }

    return nil
}

После перехода к событийной модели сервис Order просто публикует событие OrderCreated и сразу возвращает пользователю успешный ответ. Сервисы Inventory, Payment и Fulfillment асинхронно получают это событие и выполняют логику своих предметных областей.

4. Результаты и метрики

Миграция заняла 14 месяцев, но измеримые результаты появились уже за первые три. К Чёрной пятнице 2023 года новая архитектура была полностью введена в эксплуатацию.

Достигнутая доступность
99.99%
Средняя задержка p95
65мс
Рост пропускной способности
3.2x

Помимо показателей производительности, изменения существенно повлияли на организацию работы. Среднее время восстановления (MTTR) значительно сократилось, а продуктовые команды получили возможность независимо выпускать обновления несколько раз в день, не мешая друг другу.

Все публикации