Circuit breaker: предохранитель для нестабильных зависимостей

26 октября 2026 · ~9 мин чтения

надёжность распределённые-системы отказоустойчивость архитектура

Circuit breaker: предохранитель для нестабильных зависимостей

Circuit breaker (автоматический выключатель, или «предохранитель») временно прекращает вызовы к зависимому сервису после серии сбоев, чтобы дать ему восстановиться и не позволить проблеме распространиться на вызывающую систему.

История

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

Широко известное описание паттерна опубликовано Мартином Фаулером в 2014 году в статье «CircuitBreaker». Фаулер опирался на работу Майкла Найгарда о надёжных системах; второе издание его книги Release It! вышло в 2018 году. У паттерна нет единственного автора или нормативного RFC: это архитектурный приём, который библиотеки реализуют по-разному.

Важные вехи — появление библиотек отказоустойчивости для сервисных приложений, затем их перенос в облачные платформы и service mesh (сетевой слой управления взаимодействиями сервисов). Например, Netflix Hystrix популяризовал набор таких механизмов, включая circuit breaker, но проект переведён в режим обслуживания в 2018 году. В современных системах его идеи доступны в Resilience4j для Java, Polly для .NET, а также в прокси и mesh-платформах. Владельца у самого паттерна нет; конкретные реализации поддерживают соответствующие проекты.

Что это такое

Представь, что приложение вызывает платёжный сервис. Если тот замедлился, каждый запрос приложения может ждать ответа несколько секунд. При большом потоке такие ожидания занимают рабочие потоки и соединения. Очередь растёт, таймауты срабатывают, а уже перегруженное приложение тратит силы на повторные попытки. Circuit breaker следит за результатами вызовов и при заданном условии временно перестаёт отправлять новые запросы к проблемной зависимости.

У автомата обычно три состояния:

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

Circuit breaker vs retry (повторная попытка). Retry полезен при кратковременном сбое одного вызова: например, сеть потеряла пакет. Но массовые немедленные повторы создают дополнительную нагрузку. Breaker останавливает поток вызовов, когда сбой стал систематическим. Их можно сочетать: немного повторов с задержкой внутри одного вызова, а breaker — как общий предохранитель.

Circuit breaker vs rate limiting (ограничение частоты). Rate limiter ограничивает скорость запросов независимо от того, работает ли получатель. Breaker реагирует на наблюдаемые сбои конкретной зависимости. Первое защищает от чрезмерного потока, второе — от продолжения бесполезной работы при деградации.

Аналогии из жизни

  1. Электрощиток. Автомат отключает линию, когда ток опасно велик; после устранения причины цепь можно включить снова. Это хорошо передаёт идею разрыва цепи ради защиты остального. Где ломается аналогия: программный breaker обычно переключается автоматически по статистике и не гарантирует, что открытие устранит саму причину сбоя.
  2. Диспетчерская на линии поддержки. Если конкретный отдел перегружен, оператор временно перестаёт переводить туда каждый звонок и предлагает оставить заявку. Это похоже на отказ вызова и fallback. Где ломается: программная зависимость может восстановиться сама, а «оставить заявку» не всегда эквивалентно исходной операции — особенно при оплате или изменении данных.
  3. Шлюз на мосту. При опасной нагрузке въезд закрывают, чтобы мост не перегружался дальше. Где ломается: в IT порог и режим проверки задаются метриками, а запросы могут иметь разную ценность; один порог не всегда подходит всем операциям.

Как это работает

Рассмотрим клиентский сервис, обращающийся к внешнему API.

  1. Пока breaker закрыт, клиент выполняет вызов и записывает результат вместе с длительностью. Обычно используется скользящее окно (учёт последних N вызовов или последних T секунд), чтобы старые ошибки постепенно переставали влиять на решение.
  2. Реализация классифицирует исход. Таймаут, отказ соединения и некоторые серверные ошибки обычно считаются сбоями. Ошибку валидации запроса или ожидаемый ответ «не найдено» зачастую не следует учитывать: это не означает, что зависимость сломалась.
  3. Когда выполнено минимальное число наблюдений и доля либо число ошибок превышают порог, состояние становится открытым. Например, настройка может требовать не менее 20 вызовов и более половины неудач в окне. Это лишь пример конфигурации, не универсальная рекомендация.
  4. В открытом состоянии новые обращения сразу получают контролируемый отказ. Они не ждут сетевого таймаута. Вызывающая сторона может показать понятное сообщение, прочитать локальный кэш или использовать ограниченный резервный сервис — если это корректно для данной операции.
  5. По истечении времени ожидания автомат переходит в полуоткрытое состояние. Он допускает небольшой набор пробных вызовов, часто один или несколько. Остальные по-прежнему блокируются, чтобы не устроить внезапный наплыв.
  6. Если пробы успешны, автомат закрывается и обычный поток возобновляется. Если снова обнаружены ошибки, он возвращается в открытое состояние, иногда с увеличенным временем ожидания.

Упрощённо:

Closed --ошибок достаточно--> Open
Open --пауза истекла--> Half-open
Half-open --пробы успешны--> Closed
Half-open --сбой--> Open

В распределённой системе важно, где живёт состояние. Если у каждого процесса свой breaker, тысячи экземпляров могут одновременно перейти в полуоткрытое состояние и устроить «стадный эффект» (одновременный всплеск запросов). Разброс времени проверки — jitter (случайное смещение) — помогает синхронизированные пробы разнести. Общий breaker, напротив, требует координации и может сам стать сложностью.

Операции записи требуют особой осторожности. После таймаута клиент не всегда знает, выполнилась ли операция на сервере. Повтор с fallback или при пробном вызове может привести к дубликату. Для этого нужны идемпотентные операции, ключи идемпотентности или отдельная проверка статуса, а не только breaker.

Где встречается в обычной жизни

Где встречается в IT и бизнесе

Для бизнеса ценность не в том, чтобы «не показывать ошибки», а в управляемом ухудшении сервиса: основной сценарий продолжает работать, а отказ зависимости виден команде.

Кто пользуется

Паттерн применим практически в любой системе, где есть сетевые зависимости, поэтому конкретные масштабы сильно различаются. Netflix публично описывал Hystrix как часть инфраструктуры отказоустойчивости; сегодня Hystrix не следует выбирать для нового внедрения только из-за исторической известности. В Java-проектах распространён Resilience4j, в .NET — Polly. Envoy и Istio позволяют настраивать похожие ограничения на уровне прокси и service mesh.

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

Альтернативы и конкуренты

Обычно эти механизмы дополняют друг друга: timeout ограничивает ожидание, retry делает несколько разумных попыток, bulkhead изолирует ресурсы, breaker прекращает лавину обращений, а fallback определяет допустимое поведение для пользователя.

Когда НЕ стоит использовать

Связанные понятия

Литература и источники

Где встретилось у меня

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

Краткое резюме