> Как устроен оркестратор, управляющий задачами для платежей (Go)
Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: InDrive
Стек: Go
> Пример ответа
Оркестратор для платежей - это центральный компонент, который координирует выполнение распределённых задач, обеспечивая их надёжность и согласованность. В Go он часто строится на основе паттерна "сага" (Saga) с двумя подходами: хореография (события) или оркестрация (центральный координатор). Я опишу второй, как более типичный для сложных платёжных систем.
Архитектура:
- API-слой принимает запросы на создание платежа (например, POST /payments).
- Оркестратор (State Machine) управляет состоянием каждой транзакции. В Go это может быть структура с полем
stateи методами-обработчиками, которые вызываются последовательно. - Очередь задач (например, NATS, RabbitMQ или Redis Streams) для асинхронного выполнения шагов и повторных попыток.
- Хранилище состояний (PostgreSQL, Redis) для сохранения прогресса и компенсирующих действий.
Ключевые компоненты на Go:
- Модель задачи - структура с полями:
ID,State,Payload,CompensationActions. - Машина состояний - карта переходов:
Init → Authorize → Capture → CompleteилиInit → Authorize → Fail → Refund. Каждый переход - это функция, которая отправляет задачу в очередь. - Воркеры - горутины, слушающие очередь. Они выполняют шаг (например, вызов API банка) и обновляют состояние в БД. При ошибке - запускают компенсацию (откат).
- Retry-механизм - с экспоненциальной задержкой (через
time.Tickerили библиотекуcenkalti/backoff). - Идемпотентность - каждый шаг проверяет уникальный ключ (например,
paymentID:step) в Redis, чтобы избежать дублей.
Пример упрощённого кода:
GOtype PaymentOrchestrator struct {db *sql.DBqueue Queue}func (o *PaymentOrchestrator) StartPayment(ctx context.Context, paymentID string) error {// Сохраняем начальное состояниеo.db.Exec("INSERT INTO payments (id, state) VALUES ($1, 'init')", paymentID)// Отправляем первый шаг в очередьreturn o.queue.Publish("payment.steps", StepMessage{PaymentID: paymentID, Step: "authorize"})}func (o *PaymentOrchestrator) HandleStep(ctx context.Context, msg StepMessage) error {// Получаем текущее состояниеstate := o.getState(msg.PaymentID)switch msg.Step {case "authorize":err := callBankAPI(msg.PaymentID)if err != nil {o.queue.Publish("payment.steps", StepMessage{PaymentID: msg.PaymentID, Step: "refund"})return o.updateState(msg.PaymentID, "failed")}o.updateState(msg.PaymentID, "authorized")o.queue.Publish("payment.steps", StepMessage{PaymentID: msg.PaymentID, Step: "capture"})case "capture": // аналогично}return nil}
Надёжность:
- Все шаги и компенсации идемпотентны.
- Используется паттерн Outbox (таблица
outboxв БД) для гарантированной отправки сообщений в очередь. - Мониторинг через метрики (Prometheus) и алерты на зависшие задачи.
Таким образом, оркестратор на Go - это асинхронная машина состояний с очередями, ретраями и компенсациями, что обеспечивает ACID-подобную согласованность в распределённой среде.
> Похожие задачи по Go
Что такое b-tree индекс и что означает буква B
Что такое MVCC в PostgreSQL
Зачем нужен отдельный сервис на Go для оркестратора
Почему нельзя заменить канал в Go обычным слайсом сообщений?
> Похожие задачи по backend
Что такое b-tree индекс и что означает буква B
Что такое MVCC в PostgreSQL
Зачем нужен отдельный сервис на Go для оркестратора
Почему нельзя заменить канал в Go обычным слайсом сообщений?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью