@dfc, i know that you are saying that resp.Body.Close() will drain the connection if need to later reuse, but the numbers are showing the oposite. I’m not saying that you are a liar, just doing some tests and benchmarks to understand how the golang http runtime works.

Take a look at this snippet, is a little modified version from the first:

package main import ( "fmt" "html" "net/http" "sync" "time" "github.com/kr/pretty" ) var ( mutex sync.Mutex count int ) func startWebserver() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) go http.ListenAndServe(":8080", nil) } func startLoadTest() { for { resp, err := http.Get("http://localhost:8080/") if err != nil { panic(fmt.Sprintf("Got error: %v", err)) } // io.Copy(ioutil.Discard, resp.Body) resp.Body.Close() mutex.Lock() count += 1 mutex.Unlock() } } func main() { startWebserver() for i := 0; i < 1; i++ { go startLoadTest() } <-time.After(10 * time.Second) pretty.Println("request count: ", count) }

Using the snippet, with or without the io.Copy , the result of lsof is very similar to this:

lsof -p 23069 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME go 23069 diego cwd DIR 8,18 4096 14686472 /home/diego/projects/go/1.8.1/src/sample go 23069 diego rtd DIR 8,18 4096 2 / go 23069 diego txt REG 8,18 10073055 13523309 /home/diego/programs/go/1.8.1/bin/go go 23069 diego mem REG 8,18 1981712 8129743 /usr/lib/libc-2.25.so go 23069 diego mem REG 8,18 146568 8129721 /usr/lib/libpthread-2.25.so go 23069 diego mem REG 8,18 168656 8129742 /usr/lib/ld-2.25.so go 23069 diego 0u CHR 136,0 0t0 3 /dev/pts/0 go 23069 diego 1u CHR 136,0 0t0 3 /dev/pts/0 go 23069 diego 2u CHR 136,0 0t0 3 /dev/pts/0

So, my application has very few open files, but why ss -s show so many connections in closed/time wait state without the io.Copy ?

Total: 1602 (kernel 0) TCP: 14283 (estab 56, closed 14194, orphaned 23, synrecv 0, timewait 14192/0), ports 0 Transport Total IP IPv6 * 0 - - RAW 0 0 0 UDP 17 8 9 TCP 89 60 29 INET 106 68 38 FRAG 0 0 0

And almost zero with io.Copy ?

Total: 1602 (kernel 0) TCP: 70 (estab 56, closed 4, orphaned 0, synrecv 0, timewait 1/0), ports 0 Transport Total IP IPv6 * 0 - - RAW 0 0 0 UDP 16 7 9 TCP 66 43 23 INET 82 50 32 FRAG 0 0 0

Also the last line print the quantity of requests done, the version with io.Copy is almost 5x faster on my machine.

When the number of goroutines doing requests are increase to 10, the ss -s is very similar, both versions have very high number of closed/time wait connections (+/- 25k), but the version with io.Copy still is 5x faster.

Can’t understand what is happening.

The connections are being reused?

Why so many time wait/closed connections on ss -s , but they don’t show on lsof ?

Why the io.Copy is faster?

Why the time wait/closed connections go from almost 0 to 25k when i increase the concurrency with io.Copy ?

Sorry for all the questions, just trying to understand better this situation.