Библиотека Go (Golang) разработчика

@golang_lib
🔌 Circuit Breaker: Как не добить лежачего (и не умереть самому) Знакомая ситуация: внешний сервис (например, процессинг платежей или тяжелая аналитика) начинает тормозить. Ваши запросы к нему зависают по таймауту. Джунское решение: "Наверное, сеть моргнула. Добавлю-ка я ретраи!" Результат: внешний сервис и так лежит под нагрузкой, а ваши ретраи создают шторм запросов (Retry Storm), добивая его окончательно. Тем временем, в вашем сервисе копятся тысячи горутин, ожидающих ответа, исчерпываются коннекты к вашей собственной базе данных, случается OOM или паника. Поздравляю, вы получили каскадный сбой (Cascading Failure). Чтобы этого избежать, в архитектуре микросервисов используется паттерн Circuit Breaker (Предохранитель). Идея взята из электрики. Если в сети короткое замыкание - пробки выбивает, чтобы не сгорел весь дом. Как это работает (Три состояния): 1. Closed (Закрыт): Всё хорошо. Запросы идут во внешний сервис как обычно. Если случаются ошибки, предохранитель увеличивает счетчик неудач. 2. Open (Открыт): Пробили лимит ошибок (например, 5 таймаутов подряд). "Пробки выбило". Теперь Circuit Breaker перехватывает все новые запросы и моментально возвращает ошибку, даже не пытаясь сходить по сети. Профит: Мы экономим свои ресурсы (горутины не висят) и даем внешнему сервису время остыть и перезапуститься. 3. Half-Open (Полуоткрыт): Прошел таймаут (например, 10 секунд). Мы пропускаем один тестовый запрос, чтобы проверить "пульс" больного. Если запрос успешен - цепь закрывается (переходим в Closed). Если упал - снова Open. Реализация на Go: Не нужно писать конечные автоматы руками. В комьюнити есть стандарт де-факто - библиотека sony/gobreaker (да, от той самой Sony). import "github.com/sony/gobreaker" var cb *gobreaker.CircuitBreaker func init() { settings := gobreaker.Settings{ Name: "BillingAPI", MaxRequests: 1, // Сколько запросов пускать в состоянии Half-Open Timeout: 10 * time.Second, // Сколько времени висеть в состоянии Open ReadyToTrip: func(counts gobreaker.Counts) bool { // Открываем цепь, если было 5 ошибок подряд return counts.ConsecutiveFailures >= 5 }, } cb = gobreaker.NewCircuitBreaker(settings) } func GetBalance(userID int) (float64, error) { // Оборачиваем опасный сетевой вызов в cb.Execute result, err := cb.Execute(func() (interface{}, error) { return billingAPI.Fetch(userID) // Реальный поход в сеть }) if err != nil { // Если цепь открыта, cb.Execute сразу вернет gobreaker.ErrOpenState. // Идеальное место, чтобы отдать закешированное значение (Graceful Degradation)! if errors.Is(err, gobreaker.ErrOpenState) { return getBalanceFromCache(userID) } return 0, err } return result.(float64), nil } 🔥 Не суйте предохранители везде Circuit Breaker нужен исключительно для интеграций с внешними сервисами или некритичными зависимостями. Если вы попытаетесь обернуть им запросы к вашей основной базе данных (PostgreSQL), вы просто замаскируете проблему. Если лежит ваша главная БД - сервис должен лечь вместе с ней, а не пытаться делать вид, что всё нормально. #golang #architecture #microservices #circuitbreaker #systemdesign 👉 @golang_lib
Если у вас установлено приложение,
вы можете сразу перейти в канал