user811773
user811773

Reputation:

What is the theoretical max number of concurrent (simultaneous) HTTP connections in a Go server?

What is the upper limit on the number of concurrent HTTP connections that a very simple server implemented in Go can handle?

Upvotes: 15

Views: 6072

Answers (1)

user811773
user811773

Reputation:

The number of concurrent HTTP connections is limited by available memory and by operating system limits.

In Linux, the soft operating system limits - such as the maximum number of open files - can be printed out and changed by using ulimit.

In terms of memory, each HTTP connection in a minimal Go HTTP server running on 32-bit Linux consumes 21 KiB of memory (the source code of this server, compilable with Go version 2013-03-23, is below). On 64-bit Linux, the memory consumption can be expected to be higher.

On a 32-bit system with 1GB of memory available to the server, 21 KiB means that about 50,000 simultaneous connections are possible. This does not include memory consumed by the Linux kernel.

package main

import (
    "flag"
    "fmt"
    "net/http"
    "os"
    "runtime"
    "sync"
)

var isClient = flag.Bool("client", false, "Whether to start the HTTP server or the HTTP client")
var N = flag.Int("n", 1000, "Number of concurrent HTTP requests")

var wait = make(chan byte)
var counter = 0
var reachedN = make(chan byte)

func handler(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "some text")
    counter++
    if counter == *N {
        reachedN <- 0
    }
    <-wait // Block this goroutine
}

func main() {
    flag.Parse()
    if *N <= 0 {
        fmt.Fprintf(os.Stderr, "invalid number of goroutines")
        os.Exit(1)
    }

    if *isClient {
        // Initiate N http connections
        var wg sync.WaitGroup
        for i := 0; i < *N; i++ {
            wg.Add(1)
            go func(ii int) {
                _, err := http.Get("http://127.0.0.1:12345")
                if err != nil {
                    fmt.Fprintf(os.Stderr, "client %d: %s\n", ii, err)
                    os.Exit(1)
                }
                wg.Done()
            }(i)
        }
        wg.Wait()
    } else {
        runtime.GOMAXPROCS(1) // No concurrency

        // Read MemStats
        var m0 runtime.MemStats
        runtime.ReadMemStats(&m0)

        go func() {
            <-reachedN // Wait until there are *N concurrent requests

            // Read MemStats
            var m1 runtime.MemStats
            runtime.ReadMemStats(&m1)

            fmt.Printf("Number of HTTP connections:        %d\n", *N)
            fmt.Printf("Memory consumption per connection: %.2f bytes\n", float64(m1.Sys-m0.Sys)/float64(*N))
            os.Exit(1)
        }()

        http.HandleFunc("/", handler)
        err := http.ListenAndServe(":12345", nil)
        if err != nil {
        fmt.Fprintf(os.Stderr, "server: %s\n", err)
            os.Exit(1)
        }
    }
}

Upvotes: 17

Related Questions