Что такое паттерн Circuit Breaker и какую проблему он решает

Ответ

Circuit Breaker (Автоматический выключатель) — это паттерн проектирования, который используется для создания отказоустойчивых систем. Его главная цель — предотвратить каскадные сбои, когда отказ одного сервиса вызывает цепную реакцию отказов в других, зависимых от него сервисах.

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

Состояния Circuit Breaker

  1. Closed (Замкнуто): Начальное состояние. Запросы свободно проходят к сервису. Счетчик ошибок сброшен.
  2. Open (Разомкнуто): Если количество ошибок за определенный период превышает порог, выключатель переходит в это состояние. Все последующие запросы к сервису немедленно отклоняются без попытки их выполнить. Это снижает нагрузку на неисправный сервис и позволяет ему восстановиться.
  3. Half-Open (Полуоткрыто): По истечении тайм-аута выключатель переходит в это состояние. Он пропускает один или несколько тестовых запросов к сервису. Если они успешны, выключатель возвращается в состояние Closed. Если нет — снова в Open.

Пример на Python с библиотекой pybreaker

import requests
import pybreaker

# Создаем Circuit Breaker:
# - fail_max=3: разомкнется после 3 неудач подряд
# - reset_timeout=30: через 30 секунд попробует снова (перейдет в Half-Open)
breaker = pybreaker.CircuitBreaker(fail_max=3, reset_timeout=30)

@breaker
def call_unstable_api(url):
    """Функция, защищенная автоматическим выключателем."""
    print(f"Попытка вызова {url}...")
    response = requests.get(url, timeout=2)
    response.raise_for_status() # Вызовет исключение для кодов 4xx/5xx
    return response.json()

try:
    # При первых 3 сбоях будут реальные попытки вызова
    # На 4-й раз вызов будет немедленно заблокирован с исключением pybreaker.CircuitBreakerError
    data = call_unstable_api('https://httpstat.us/503')
except pybreaker.CircuitBreakerError:
    print("Circuit Breaker разомкнут! Запрос заблокирован.")
except requests.exceptions.RequestException as e:
    print(f"Ошибка сети: {e}")

Ответ 18+ 🔞

А, слушай, вот эта штука — Circuit Breaker, она же «Автоматический выключатель». Ну, представь себе, ты пишешь систему, а там один сервис, как последний мудак, начинает тупить и падать. И если ты будешь упорно в него стучаться, как дурак, то он не только сам сдохнет, но и тебя за собой в пизду утянет, а за тобой — все остальные. Каскадный сбой, ёпта! Вся архитектура накрывается медным тазом.

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

Как он, блядь, работает? Три состояния:

  1. Closed (Замкнут): Всё пиздато. Запросы летят к сервису как ни в чём не бывало. Счётчик ошибок — ноль.
  2. Open (Разомкнут): Вот тут начинается магия. Если этот ёбаный сервис накосячил уже, скажем, три раза подряд — ХРЯСЬ! — выключатель щёлкает. Все новые запросы получают отлуп сразу, даже не пытаясь достучаться до этого умирающего урода. «Иди нахуй, — говорит выключатель, — дай ему передохнуть, а нам — не тратить силы впустую». Гениально же!
  3. Half-Open (Полуоткрыт): Прошло, допустим, 30 секунд. Выключатель такой: «Ну что, мудак, очухался?». Он переходит в этот режим и осторожно, как на минное поле, пропускает один пробный запрос. Если тот прошёл — ура, всё живое, снова Closed. Если опять обосрался — ну всё, пидарас, снова Open, жди ещё.

Смотри, как это в коде выглядит, на питоне с pybreaker:

import requests
import pybreaker

# Создаём нашего сторожа:
# fail_max=3 — сломается после трёх косяков.
# reset_timeout=30 — через полминуты попробует «пощупать» сервис снова.
breaker = pybreaker.CircuitBreaker(fail_max=3, reset_timeout=30)

@breaker
def call_unstable_api(url):
    """Функция, которую этот выключатель будет охранять, как зеницу ока."""
    print(f"Попытка вызова {url}...")
    response = requests.get(url, timeout=2)
    response.raise_for_status() # Если сервер ответил ошибкой — это наш косяк!
    return response.json()

try:
    # Первые три раза, если сервис глючит, будут реальные попытки.
    # На четвёртый — выключатель уже разомкнётся и просто выбросит pybreaker.CircuitBreakerError.
    data = call_unstable_api('https://httpstat.us/503')
except pybreaker.CircuitBreakerError:
    print("Circuit Breaker разомкнут! Запрос заблокирован.")
except requests.exceptions.RequestException as e:
    print(f"Ошибка сети: {e}")

Вот и вся фишка. Вместо того чтобы тупо долбиться в закрытую дверь, ты ставишь умную защёлку, которая говорит: «Э, дружок, там никого, иди нахуй, не мешай человеку в сортире сидеть». А когда из-за двери доносятся обнадёживающие звуки смыва — можно снова постучать. Красота, блядь!