Как реализовать обработку ошибок в микросервисах
Что проверяют
Понимаете ли вы, что в распределённой системе сбой это норма: сеть рвётся, соседний сервис тормозит, сообщение приходит дважды. Следом обычно спрашивают, какие ошибки можно повторять, а какие нельзя, и как не устроить каскадный отказ.
Как отвечать
Разложите ответ по уровням. Синхронные вызовы между сервисами:
- таймауты всегда:
requestsпо умолчанию ждёт бесконечно, уhttpxтаймаут по умолчанию 5 секунд. Задавайте их явно, раздельно на соединение и чтение, и укладывайте в общий бюджет исходного запроса; - повторы с умом:
tenacityс экспоненциальной задержкой и джиттером, только на временные ошибки (таймаут, 502, 503, 429 с учётомRetry-After) и только для идемпотентных операций. Для POST передают ключ идемпотентности; - circuit breaker: после серии отказов перестаём звать больной сервис и сразу отдаём fallback, через паузу пробуем снова. В Python есть
pybreaker, но часто это делают на уровне service mesh или шлюза; - единый формат ошибки: в FastAPI через
@app.exception_handler, в духе Problem Details (RFC 9457), с кодом ошибки, который понимают другие сервисы.
Асинхронное взаимодействие через очереди: повторы с задержкой, после N попыток сообщение уходит в dead letter queue, обработчики идемпотентны, потому что доставка почти всегда «как минимум один раз». Для распределённых операций берут сагу с компенсирующими действиями и outbox, чтобы запись в базу и отправка события не разъехались.
Наблюдаемость: сквозной trace id через OpenTelemetry, структурные логи, метрики ошибок по сервисам и алерты на их рост.
Где спотыкаются
Повторяют всё подряд, включая 400 и неидемпотентные операции. Итог: двойные списания и лавина повторов, которая добивает и без того перегруженный сервис.
Пишут await httpx.get(url). Функция httpx.get синхронная, в асинхронном коде нужен httpx.AsyncClient, созданный один раз на приложение, а не на каждый запрос.
Называют Prometheus системой логирования. Он собирает метрики, а логи хранят Loki, Elasticsearch или аналоги, и разбирать инцидент нужно по обоим источникам.