АналитикаСистемный анализСистемный аналитик интеграционных решений

Внешняя система начинает отвечать с ошибками: назовите механизм, который предотвращает каскадный отказ вызы...

Внешняя система начинает отвечать с ошибками: назовите механизм, который предотвращает каскадный отказ вызывающего сервиса.

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Для защиты вызывающего сервиса применяют паттерн Circuit Breaker («предохранитель»). Он отслеживает сбои внешней зависимости, временно прекращает новые вызовы после превышения заданного порога и периодически проверяет, восстановилась ли система.

Исторический контекст

Паттерн возник как применение идеи электрического автоматического выключателя к распределённым системам. Исходная проблема заключалась в том, что отказ одной зависимости может распространяться по цепочке: растёт число ожидающих запросов, исчерпываются потоки и соединения, после чего начинают отказывать уже здоровые компоненты.

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

Постановка проблемы

Предположим, сервис оформления заказа синхронно обращается к внешней системе расчёта доставки. Если она недоступна, каждый входящий запрос может ждать тайм-аута, а затем повторять попытку.

Без ограничения такой нагрузки очередь ожидающих операций быстро растёт. В результате сервис тратит ресурсы не на обработку полезных запросов, а на бесперспективные вызовы, что создаёт каскадный отказ.

Подробное решение

Circuit Breaker обычно имеет три состояния:

  • Closed — вызовы разрешены, компонент собирает статистику успешных и неуспешных обращений.
  • Open — вызовы к зависимости немедленно отклоняются или переводятся на заранее определённый резервный сценарий.
  • Half-Open — после выдержки паузы пропускается ограниченное число проверочных вызовов. Успех возвращает автомат в состояние Closed, повторный отказ оставляет его в Open.

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

Предохранитель не устраняет первопричину и не гарантирует восстановление внешней системы. Он ограничивает ущерб: быстро отказывает вместо длительного ожидания, освобождает ресурсы и позволяет вызывающему сервису применить понятную деградацию — показать сохранённые данные, поставить операцию в очередь или вернуть временную ошибку.

Circuit Breaker нужно согласовать с тайм-аутами, ограничением параллелизма и повторными попытками. Агрессивные ретраи способны сами поддерживать перегрузку, поэтому их ограничивают, используют экспоненциальную задержку и применяют только к операциям, безопасным для повторения. Нельзя автоматически возвращать устаревшие или неполные данные там, где это нарушает бизнес-инварианты.

Переходы состояний и причины отказов должны наблюдаться через метрики и журналы. Важно различать техническую недоступность, отказ авторизации, ошибку валидации и бизнес-отказ: открытие предохранителя из-за ожидаемых ошибок клиента может скрыть дефект в контракте или неверную интеграцию.

Ситуация из практики

Сервис бронирования обращается к поставщику гостиниц. Во время аварии поставщик отвечает через 20 секунд вместо обычных 300 миллисекунд, а входящий поток составляет сотни запросов в секунду.

Первый вариант — увеличить тайм-аут. Он снижает число преждевременных ошибок, но увеличивает количество занятых ресурсов и ускоряет исчерпание пулов потоков и соединений.

Второй вариант — бесконечно повторять запросы. Это может повысить вероятность успеха при кратком сбое, но во время перегрузки создаёт дополнительный трафик и усугубляет отказ.

Третий вариант — внедрить Circuit Breaker с коротким тайм-аутом, ограниченными повторами и режимом деградации: для поиска показывать кэшированные предложения, а подтверждение бронирования временно отклонять с понятным статусом. После серии отказов новые вызовы к поставщику не выполняются, поэтому сервис сохраняет способность обслуживать остальные функции.

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

Что кандидаты часто упускают

1. Достаточно ли Circuit Breaker для защиты сервиса от медленной зависимости?

Нет. Предохранитель должен работать вместе с явным тайм-аутом, иначе зависший вызов может не считаться ошибкой достаточно долго. Дополнительно применяют ограничение числа одновременных обращений, чтобы даже разрешённые вызовы не заняли все ресурсы.

2. Что произойдёт, если все экземпляры сервиса откроют предохранитель одновременно?

После общей аварии они могут синхронно перейти в Half-Open и создать всплеск проверочных запросов. Для снижения риска используют случайное распределение задержек, ограничение числа пробных вызовов и постепенное восстановление трафика. Настройки должны учитывать масштаб развертывания, а не только поведение одного экземпляра.

3. Можно ли считать любой ответ с ошибкой причиной для открытия предохранителя?

Нет. Ошибки валидации, отсутствие прав или корректный бизнес-отказ обычно показывают, что запрос обработан системой и повторять его бессмысленно. В статистику недоступности зависимости включают сетевые ошибки, тайм-ауты и соответствующие классы серверных ошибок, определив это явно в контракте интеграции.