Утечка памяти при множественных соединениях в net/http Golang

У меня есть следующая функция:

var client = &http.Client{
    Timeout: time.Millisecond * 100,
}

func scan(ip string) (error, string, string, string) {
    resp, err := client.Get("http://" + ip)

    // Закрытие неиспользуемых соединений
    client.CloseIdleConnections()

    if err != nil {
        return err, "", "", ""
    }

    headers := buildHeaders(resp.Header)
    body, _ := io.ReadAll(resp.Body)

    // закрытие тела ответа
    resp.Body.Close()

    return err, headers, string(body), resp.Status
}

Данный код приводит к огромной утечке памяти. В процессе дебага выяснилось что в net/http в функции queueForIdleConn() в файле transport.go происходит следующее:

if t.idleConnWait == nil {
    t.idleConnWait = make(map[connectMethodKey]wantConnQueue)
}
q := t.idleConnWait[w.key]
q.cleanFront()
q.pushBack(w)
t.idleConnWait[w.key] = q
return false

Получается так что в idleConnWait = {map[http.connectMethodKey]http.wantConnQueue} постоянно записываются новые элементы, но судя по размеру никогда не освобождается. Как правильно сделать так что бы этот массив очищался?

Примечания:

  1. Программа кидает соединения множеству адресов 95% которых недействительны (не отвечают).
  2. Эта функция запускается сразу в большом кол-ве горутин (go scan())

Ответы (1 шт):

Автор решения: Borislav
  1. Ну идиоматично использовать для закрытия defer resp.Body.Close(), т.к. в неких пограничных случая, если даже вам вернулась ошибка, тело запроса может быть открыто.

  2. Объявите свой собственный транспорт, там вы сможете ограничить размерность оной маппы:

     var client = &http.Client{
         Timeout: time.Millisecond * 100,
         Transport: &http.Transport{
             MaxIdleConns:        100,
             MaxIdleConnsPerHost: 10,
         },
     }
    
  3. Если в вашем случае повторное использование соединений не критично, вы можете полностью отключить его, установив параметр DisableKeepAlives в true.

    var client = &http.Client{
       Timeout: time.Millisecond * 100,
       Transport: &http.Transport{
           DisableKeepAlives: true,
       },
    }
    
→ Ссылка