Ответ
Да, я применял паттерн Circuit Breaker (Предохранитель) в микросервисной архитектуре для повышения отказоустойчивости системы.
Что это?
Circuit Breaker — это паттерн, который предотвращает повторные вызовы потенциально неработающего сервиса. Он работает как электрический предохранитель: после определенного количества сбоев он «размыкает цепь» и перестает отправлять запросы на проблемный сервис на некоторое время, возвращая ошибку немедленно. Это защищает систему от каскадных сбоев и снижает нагрузку на отказавший компонент.
Ключевые состояния:
Closed
(Замкнут): Изначальное состояние. Запросы свободно проходят к сервису. Если количество сбоев превышает порог, предохранитель переходит в состояниеOpen
.Open
(Разомкнут): Запросы к сервису немедленно блокируются, клиенту возвращается ошибка. По истечении тайм-аута предохранитель переходит в состояниеHalf-Open
.Half-Open
(Полуоткрыт): Пропускается один тестовый запрос. Если он успешен, предохранитель возвращается вClosed
. Если нет — снова вOpen
.
Когда использовать?
Я использовал его при взаимодействии между микросервисами, особенно для вызовов критически важных, но потенциально нестабильных зависимостей:
- Вызовы внешних API (платежные шлюзы, сервисы геолокации).
- Запросы к перегруженной базе данных или кэшу.
- Синхронное общение между внутренними сервисами.
Пример с библиотекой sony/gobreaker
:
import (
"fmt"
"net/http"
"time"
"github.com/sony/gobreaker"
)
// Создаем Circuit Breaker с настройками
var cb = gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "HTTP-GET",
MaxRequests: 1, // Количество запросов в состоянии Half-Open
Interval: 0, // Период сброса счетчиков (0 - не сбрасывать)
Timeout: 10 * time.Second, // Период, после которого состояние меняется с Open на Half-Open
ReadyToTrip: func(counts gobreaker.Counts) bool {
// Переходим в Open после 3 последовательных сбоев
return counts.ConsecutiveFailures > 3
},
})
func makeRequest() (interface{}, error) {
// Защищаем вызов внешнего сервиса
body, err := cb.Execute(func() (interface{}, error) {
resp, err := http.Get("http://example.com/api")
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 500 {
return nil, fmt.Errorf("service returned error: %s", resp.Status)
}
// ... обработка успешного ответа
return []byte("OK"), nil
})
if err != nil {
// Если ошибка - это разрыв цепи, обрабатываем ее
if err == gobreaker.ErrOpenState {
fmt.Println("Circuit Breaker is open. Service is unavailable.")
}
return nil, err
}
return body, nil
}