Jay Taylor's notes

back to listing index

Go HTTP: Too Many Open Files

[web search]
Original source (craigwickesser.com)
Tags: golang go http file-descriptors lsof sockets keepalive craigwickesser.com
Clipped on: 2017-04-10

2015-01-07 | tags:posts Development golang categories:posts

Go HTTP: Too Many Open Files

I recently had a situation where an application I was working on in Go would run into the “too many open files” error. After some debugging, I discovered it wasn’t closing HTTP connections being made to an HTTP server.

Update: It turns out, I was testing against “test” server which wasn’t setting the Connection: close header, therefore neither the client or server was requesting the connection to be closed. If the server had set the Connection header, things would’ve been fine.

The code looked like this:

  transport := &http.Transport {
    Proxy: http.ProxyFromEnvironment,
    Dial: (&net.Dialer{
      Timeout: 0,
      KeepAlive: 0
    }).Dial,
    TLSHandshakeTimeout: 10 * time.second
  }

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

  function submitData(url string, fileReader io.Reader) error {
    req, reqErr := http.NewRequest("POST", url, fileReader)
    if reqErr != nil {
      return reqErr
    }
    req.Header.Set("Content-Type", "application/octet-stream")

    resp, err := httpClient.Do(req)
    if err != nil {
      return err
    }
    defer resp.Body.Close()

    // read the body

    return nil
  }

The submitData function gets called over and over whenever new data needs to be POSTed. Using netstat I noticed the connections to the server were staying ESTABLISHED instead of being closed. I set the “timeout” on the dialer to 0, because the POST should block until the server is done and responds (which is an undetermined amount of time).

I know the server is responding, because the code that processes the response was in fact getting data.

It turns out, HTTP/1.1 uses persistent connections by default:

A significant difference between HTTP/1.1 and earlier versions of HTTP is that persistent connections are the default behavior of any HTTP connection.

http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html

The solution was to inform the server that the client wants to close the connection after the transaction is complete. This can be done by setting the Connection header,

req.Header.Set("Connection", "close")

or by setting the Close property to true on the http.Request:

req.Close = true

After doing that, the “too many open files” issue went away as the program was no longer keeping HTTP connections open and thus not using up file descriptors.