Ограничение количества одновременно работающих горутин

Основные риски — память, очередь планировщика, соединения и перегрузка внешних серверов. Ограничивайте число активных работников, тайм-ауты и скорость запросов к одному хосту.

Короткий ответ

Что ответить на собеседовании

Основные риски — память, очередь планировщика, соединения и перегрузка внешних серверов. Ограничивайте число активных работников, тайм-ауты и скорость запросов к одному хосту.

Подробный разбор

Ответ с пояснениями

Условие

Программа получает список ссылок и загружает их параллельно с помощью горутин. При попытке обработать огромный набор адресов (например, 100 000) могут возникнуть проблемы: из‑за большого числа одновременно запущенных горутин растёт потребление памяти, увеличивается нагрузка на планировщик и может произойти истощение системных ресурсов (сокеты, файловые дескрипторы). Чтобы избежать этих проблем, следует ограничить количество одновременно активных горутин, используя семафоры, пул воркеров или ограниченный канал.

package main

import (
    "fmt"
    "net/http"
    "time"
    "sync"
)

func main() {
    links := []string{
        "https://www.lamoda.ru",
        "https://www.yandex.ru",
        "https://www.mail.ru",
        "https://www.google.com",
    }

    wgGroup := &sync.WaitGroup{}

    for _, link := range links {
        wgGroup.Add(1)
        go func(url string) {
            defer wgGroup.Done()

            fmt.Printf("Fetching %s...\n", url)

            err := fetchUrl(url)
            if err != nil {
                fmt.Printf("Error fetching %s: %v\n", url, err)
                return
            }

            fmt.Printf("Fetched %s\n", url)
        }(link)
    }

    fmt.Println("All requests launched!")
    wgGroup.Wait()
    fmt.Println("Program finished.")
}

func fetchUrl(url string) error {
    // Подробная реализация опущена и не относится к теме задачи
    // _, err := http.Get(url)
    return nil
}

Вопрос: какие сложности могут появиться при масштабировании до 100 000 URL и какие приёмы помогут их решить?

Ответ

Основные риски — память, очередь планировщика, соединения и перегрузка внешних серверов. Ограничивайте число активных работников, тайм-ауты и скорость запросов к одному хосту.

Минимальный семафор захватывается ДО запуска горутины:

sem := make(chan struct{}, 16)
var wg sync.WaitGroup
for _, link := range links {
    sem <- struct{}{}
    wg.Add(1)
    go func(url string) {
        defer wg.Done()
        defer func() { <-sem }()
        if err := fetchUrl(url); err != nil { fmt.Println(url, err) }
    }(link)
}
wg.Wait()

Захват внутри горутины ограничил бы запросы, но не число созданных горутин. fetchUrl должна иметь тайм-аут, закрывать response body и возвращать ошибку. В исходном сокращённом файле уберите неиспользуемые импорты.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее