| Канал | Публикаций | Подписчиков | Последний пост |
|---|---|---|---|
Библиотека Go (Golang) р…
[telegram]
|
6 | 2705 | 22.06.26 |
|
Библиотека программиста
[max]
|
7 | 1832 | 20.06.26 |
|
C++ Developer • Cpp Syst…
[max]
|
8 | 2145 | 20.06.26 |
|
Python академия
[max]
|
8 | 2989 | 20.06.26 |
|
Python Разработка | Web …
[max]
|
8 | 2874 | 20.06.26 |
|
Программирование ITmozg
[max]
|
8 | 1552 | 20.06.26 |
|
Bash Советы - Bash Scrip…
[max]
|
8 | 2244 | 20.06.26 |
Нет данных о рекламе
Загрузка данных...
| Размещенный пост | Текст публиакции | Рекламирующий канал | Просмотры | Просмотры 24 ч | Прирост подписчиков |
|---|
Загрузка данных...
| Размещенный пост | Текст публикации | Рекламируемый канал | Просмотры | Просмотры 24 ч | Прирост подписчиков |
|---|
| Дата и время публикации | Текст публикации | Рекламируемый канал | Динамика просмотров | Всего просмотров |
|---|---|---|---|---|
| 2026-06-22 08:42:02 | Connection Pool: Как Go убивает базу данных (и как этого избежать) Выкатываете вы новый сервис, запускаете нагрузочное тестирование, и тут логи начинают истекать кровью: FATAL: sorry, too many clients already. Вы бежите к админам (или в консоль AWS) и видите, что ваш скромный сервис на Go открыл 1500 соединений к PostgreSQL и положил базу. Почему так вышло? Потому что по умолчанию стандартный пакет database/sql не имеет лимита на количество открытых соединений. Если к вам прилетит 1000 запросов одновременно, Go честно попытается открыть 1000 TCP-соединений. Для БД каждый коннект - это отдельный тяжелый процесс, который жрет память. Чтобы не быть врагом своим девопсам, нужно всегда настраивать пул соединений. Это делается тремя магическими методами. 1. SetMaxOpenConns(n) - Ограничение жадности Это жесткий лимит на количество одновременно открытых соединений к базе. Если вы поставили лимит 50, а пришел 51-й запрос, горутина просто заблокируется и будет покорно ждать, пока кто-нибудь не освободит коннект (или пока не отвалится по таймауту контекста). 2. SetMaxIdleConns(n) - Пул в режиме ожидания Сколько соединений держать открытыми, когда нет нагрузки? Если поставить слишком мало, при скачке трафика Go начнет судорожно устанавливать новые TCP-соединения (это долгий хендшейк, потеря драгоценных миллисекунд). Золотое правило: Часто MaxIdleConns ставят равным MaxOpenConns. Это гарантирует, что пул всегда прогрет и готов к бою. 3. SetConnMaxLifetime(d) - Защита от тухлых коннектов Если коннект висит слишком долго, база данных, балансировщик (HAProxy) или файрвол могут закрыть его в одностороннем порядке. Go об этом не узнает. Когда приложение попытается послать запрос в такой коннект, вы получите классическую ошибку driver: bad connection. Заставьте Go принудительно пересоздавать коннекты раз в час или несколько минут. Как это выглядит в коде: db, err := sql.Open("postgres", dsn) if err != nil { log.Fatal(err) } // Задаем лимиты в зависимости от размера вашего инстанса БД // и количества запущенных подов сервиса. db.SetMaxOpenConns(50) db.SetMaxIdleConns(50) db.SetConnMaxLifetime(30 * time.Minute) // Опционально: время жизни коннекта в простое (Go 1.15+) db.SetConnMaxIdleTime(5 * time.Minute) 🔥 Senior Tip: PgBouncer Если у вас 20 подов микросервиса, и каждый имеет MaxOpenConns = 50, суммарно в базу может прилететь 1000 коннектов. Postgres от такого станет плохо (оптимально для него - пара сотен коннектов). Поэтому в мире взрослых нагрузок между Go и базой всегда ставят PgBouncer (в режиме transaction pooling). Он мультиплексирует тысячи легковесных клиентских коннектов в десятки реальных коннектов к базе. В таком случае в Go можно ставить лимиты побольше, а общую нагрузку будет сглаживать PgBouncer. У кого базы падали в пятницу вечером из-за дефолтных настроек sql.DB? #golang #database #architecture #performance #bestpractices 👉 @golang_lib Connection Pool:… | — |
|
247 |
| 2026-06-19 07:48:16 | 🔌 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 🔌 Circuit Breake… | — |
|
324 |
Загрузка данных...
| Время | Контент | Подписчиков | Кто ссылался | Просмотры 48ч | Просмотры 24ч |
|---|