← Назад к вопросам

Что такое Circuit breaker?

2.3 Middle🔥 172 комментариев
#Микросервисы и архитектура

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Circuit Breaker (Автоматический выключатель)

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

Основные цели и преимущества

  • Предотвращение каскадных отказов: Локализует сбой в одном сервисе, не позволяя ему распространиться на всю систему.
  • Снижение нагрузки на зависимые сервисы: Даёт возможность "заболевшему" сервису восстановиться без постоянных запросов.
  • Улучшение отзывчивости: При открытом состоянии выключателя система мгновенно возвращает ошибку или значение по умолчанию, не дожидаясь таймаута.
  • Автоматическое восстановление: Позволяет периодически проверять восстановление сервиса и возобновлять работу.

Состояния Circuit Breaker

Типичная реализация имеет три состояния:

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

Реализация на Go (пример с использованием пакета sony/gobreaker)

package main

import (
    "context"
    "errors"
    "fmt"
    "time"

    "github.com/sony/gobreaker"
)

// Имитация клиента к внешнему API
type UnstableServiceClient struct {
    failureRate float64 // Вероятность ошибки (от 0 до 1)
}

func (c *UnstableServiceClient) Call(ctx context.Context) (string, error) {
    // Имитация нестабильного вызова
    if time.Now().UnixNano()%100 < int64(c.failureRate*100) {
        return "", errors.New("service unavailable")
    }
    return "success", nil
}

func main() {
    client := &UnstableServiceClient{failureRate: 0.7} // Сервис падает в 70% случаев

    // Настройка Circuit Breaker
    cb := gobreaker.NewCircuitBreaker(gobreaker.Settings{
        Name:        "ExternalAPI",
        MaxRequests: 3,                  // Кол-во разрешённых запросов в Half-Open
        Timeout:     5 * time.Second,    // Длительность состояния Open перед Half-Open
        ReadyToTrip: func(counts gobreaker.Counts) bool {
            // Переход в Open при 5 последовательных ошибках
            return counts.ConsecutiveFailures > 5
        },
        OnStateChange: func(name string, from, to gobreaker.State) {
            // Логирование смены состояния
            fmt.Printf("CircuitBreaker '%s' changed from %s to %s\n", name, from, to)
        },
    })

    // Симуляция потока запросов
    for i := 0; i < 20; i++ {
        time.Sleep(300 * time.Millisecond)

        result, err := cb.Execute(func() (interface{}, error) {
            // Обёртка для защищаемого вызова
            return client.Call(context.Background())
        })

        if err != nil {
            // Если сработал Circuit Breaker (gobreaker.ErrOpenState) или ошибка сервиса
            if errors.Is(err, gobreaker.ErrOpenState) {
                fmt.Printf("[%d] Request rejected (Circuit Breaker OPEN)\n", i)
            } else {
                fmt.Printf("[%d] Service error: %v\n", i, err)
            }
            continue
        }

        fmt.Printf("[%d] Success: %v\n", i, result)
    }
}

Ключевые параметры настройки

  • Порог срабатывания (ReadyToTrip): Определяет условия перехода в состояние Open (например, counts.TotalFailures > 10).
  • Таймаут восстановления (Timeout): Время, в течение которого выключатель остаётся открытым перед переходом в Half-Open.
  • Количество пробных запросов (MaxRequests): Сколько запросов разрешено в состоянии Half-Open для проверки восстановления.
  • Интервал сброса счетчиков: Период, после которого счётчики успехов/ошибок обнуляются (обычно реализуется через скользящее окно).

Практические аспекты использования в Go-экосистеме

  1. Интеграция: Чаще всего Circuit Breaker внедряется на уровне клиента HTTP (например, оборачивая http.Client) или gRPC-интерсептора.
  2. Логирование и мониторинг: Критически важно логировать все смены состояний и собирать метрики (количество запросов в каждом состоянии, процент срабатываний), например, через Prometheus.
  3. Fallback-стратегии: В состоянии Open система должна возвращать осмысленное значение:
    *   Кэшированные данные
    *   Значения по умолчанию
    *   Упрощённую бизнес-логику
    *   Ошибку, понятную пользователю
  1. Распространённые библиотеки: Помимо sony/gobreaker, используются afex/hystrix-go (вдохновлённый Hystrix от Netflix) и встроенные решения в фреймворках типа GoKit.

Отличие от Retry-паттернов

Важно не путать Circuit Breaker с паттерном Retry. Retry пытается повторить неудачный запрос, что уместно при временных сбоях (timeout, network glitch). Circuit Breaker, наоборот, прекращает делать запросы при устойчивом сбое. Эти паттерны часто используют вместе: сначала Retry для единичных ошибок, а при их накоплении — срабатывает Circuit Breaker.

В современных микросервисных архитектурах на Go Circuit Breaker является не просто удобным шаблоном, а необходимым элементом отказоустойчивости, который наряду с Retry, Timeouts и Rate Limiting формирует стратегию устойчивости (Resilience) приложения. Его правильная настройка требует анализа конкретных SLA зависимых сервисов и допустимости деградации функциональности.