2015-07-31 10 views
6

Sto caricando pagine Web utilizzando un pool di thread semplice, caricando dinamicamente gli URL dal file. Ma questo piccolo programma alloca lentamente tutta la memoria del mio server, fino a quando l'assassino non lo ferma. Sembra resp.Body.Close() non libera memoria per il corpo del testo (dimensione della memoria ~ pagine scaricate * dimensione della pagina avg). Come posso forzare golang a liberare memoria allocata per il testo html del corpo?Go non rilascia memoria dopo http.Get

package main 

import (
    "bufio" 
    "fmt" 
    "io/ioutil" 
    "net/http" 
    "os" 
    "strings" 
    "sync" 
) 

func worker(linkChan chan string, wg *sync.WaitGroup) { 
    defer wg.Done() 

    for url := range linkChan { 
     // Getting body text 
     resp, err := http.Get(url) 
     if err != nil { 
      fmt.Printf("Fail url: %s\n", url) 
      continue 
     } 
     body, err := ioutil.ReadAll(resp.Body) 
     resp.Body.Close() 
     if err != nil { 
      fmt.Printf("Fail url: %s\n", url) 
      continue 
     } 
     // Test page body 
     has_rem_code := strings.Contains(string(body), "googleadservices.com/pagead/conversion.js") 
     fmt.Printf("Done url: %s\t%t\n", url, has_rem_code) 
    } 
} 

func main() { 
    // Creating worker pool 
    lCh := make(chan string, 30) 
    wg := new(sync.WaitGroup) 

    for i := 0; i < 30; i++ { 
     wg.Add(1) 
     go worker(lCh, wg) 
    } 

    // Opening file with urls 
    file, err := os.Open("./tmp/new.csv") 
    defer file.Close() 
    if err != nil { 
     panic(err) 
    } 
    reader := bufio.NewReader(file) 

    // Processing urls 
    for href, _, err := reader.ReadLine(); err == nil; href, _, err = reader.ReadLine() { 
     lCh <- string(href) 
    } 

    close(lCh) 
    wg.Wait() 
} 

Ecco un output da strumento pprof:

 flat flat% sum%  cum cum% 
    34.63MB 29.39% 29.39% 34.63MB 29.39% bufio.NewReaderSize 
     30MB 25.46% 54.84%  30MB 25.46% net/http.(*Transport).getIdleConnCh 
    23.09MB 19.59% 74.44% 23.09MB 19.59% bufio.NewWriter 
    11.63MB 9.87% 84.30% 11.63MB 9.87% net/http.(*Transport).putIdleConn 
    6.50MB 5.52% 89.82%  6.50MB 5.52% main.main 

Sembra this issue, ma è fisso 2 anni fa.

+0

Quale versione di Go stai usando? – JimB

+2

(probabilmente non correlato, ma 'lCh' non è memorizzato nel buffer, quindi queueLen sarà sempre 0) – JimB

+0

Vai versione 1.4.2 – Denton

risposta

4

Trovato la risposta in this thread su golang-nuts. http.Transport salva le connessioni per il futuro riutilizzo in caso di richiesta allo stesso host, causando gonfiore della memoria nel mio caso (centinaia di migliaia di host diversi). Ma disabilitare KeepAlives risolve completamente questo problema.

codice di lavoro:

func worker(linkChan chan string, wg *sync.WaitGroup) { 
    defer wg.Done() 

    var transport http.RoundTripper = &http.Transport{ 
     DisableKeepAlives: true, 
    } 

    c := &http.Client{Transport: transport} 

    for url := range linkChan { 
     // Getting body text 
     resp, err := c.Get(url) 
     if err != nil { 
      fmt.Printf("Fail url: %s\n", url) 
      continue 
     } 
     body, err := ioutil.ReadAll(resp.Body) 
     resp.Body.Close() 
     if err != nil { 
      fmt.Printf("Fail url: %s\n", url) 
      continue 
     } 
     // Test page body 
     has_rem_code := strings.Contains(string(body), "googleadservices.com/pagead/conversion.js") 
     fmt.Printf("Done url: %s\t%t\n", url, has_rem_code) 
    } 
} 
+3

Ti mancano alcuni degli altri valori predefiniti da "http.DefaultTransport" che potrebbero essere utili, come il Dialer con un timeout. Inoltre, se si eseguono spesso richieste anche agli stessi host, è possibile che si desideri avere keepalive e utilizzare invece Transport.CloseIdleConnections. – JimB

+1

Certo, nel codice vero sto anche usando 'Dialer' con timeout, ma è irrilevante per la soluzione, quindi ho deciso di saltare questa parte. Poiché tutte le mie richieste vanno a host diversi, l'uso di 'Transport.CloseIdleConnections' non ha senso in questo momento, ma può essere utile a volte. Grazie! – Denton