👁 В проде утечки горутин — это не абстрактная проблема, а тихий убийца памяти и файловых дескрипторов. Особенно часто это происходит, когда одна из параллельных задач падает, а остальные продолжают жить своей жизнью. Решение — централизованная отмена через context и координация через errgroup.
📝 Управление конкурентными задачами с корректной отменой
package main
import (
"context"
"fmt"
"net/http"
"time"
"golang.org/x/sync/errgroup"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
g, ctx := errgroup.WithContext(ctx)
urls := []string{
"https://service1.local",
"https://service2.local",
}
for _, url := range urls {
url := url
g.Go(func() error {
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
fmt.Println(url, resp.StatusCode)
return nil
})
}
if err := g.Wait(); err != nil {
fmt.Println("ошибка:", err)
}
}📌 Как это работает:
errgroup.WithContextавтоматически отменяет общий контекст, если любая горутина возвращает ошибку.- Все HTTP-запросы привязаны к этому контексту, поэтому при отмене они корректно прерываются.
g.Wait()гарантирует, что мы не выйдем из main, пока все горутины не завершатся.
❗️ Такой подход защищает от висящих горутин, зависших запросов и неконтролируемого роста ресурсов. В сервисах с параллельными запросами или fan-out архитектурой это не «красота кода», а базовая гигиена продакшена.
tags: #go #разработка #автоматизация



