0

I tried to measure the bandwidth of Go default HTTP server implementation on my local machine. The server is just accepting any HTTP request, increments the counter using sync.atomic and send 200 OK response. Also, the server collects amount of requests every second, prints it and resets counter to zero:

type hand struct {
    cnt int32
}

func (h *hand) ServeHTTP(rsp http.ResponseWriter, req *http.Request) {
    atomic.AddInt32(&h.cnt, 1)
    rsp.WriteHeader(200)
    if req.Body != nil {
        req.Body.Close()
    }
}

func main() {
    h := new(hand)
    s := &http.Server{
        Addr:    ":8080",
        Handler: h,
    }
    ticker := time.NewTicker(1 * time.Second)
    go func() {
        for tick := range ticker.C {
            val := atomic.SwapInt32(&h.cnt, 0)
            fmt.Printf("(%v) %d RPS\n", tick, val)
        }
    }()
    log.Fatal(s.ListenAndServe())
}

The target client is trying to send 100000 GET requests simultaneously:

const total = 100000

func main() {
    var r int32
    var rsp int32
    r = total
    rsp = r
    for r > 0 {
        go func() {
            p, err := http.Get("http://localhost:8080")
            atomic.AddInt32(&rsp, -1)
            if err != nil {
                fmt.Printf("error: %s\n", err)
                return
            }
            if p.StatusCode != 200 {
                fmt.Printf("status %d\n", p.StatusCode)
            }
        }()
        r--

    }
    for {
        x := atomic.LoadInt32(&rsp)
        fmt.Printf("sent : %d\n", total-x)
        if x == 0 {
            return
        }
        time.Sleep(1 * time.Second)
    }
}

I'm using Linux machine with 5.3.2-gentoo kernel. I changed ulimits (both soft and hard) of nofile to 100000. When I run this tests, all other user applications were stopped.

I'm not expecting to get accurate results, but just need to know the level of this threshold, something like X000 or X0000 or X00000.

But the server can't process more than 4000 requests per second, it's looking too low:

# removed timestamps
0 RPS
0 RPS
0 RPS
3953 RPS
3302 RPS
387 RPS
37 RPS
1712 RPS

How can I raise the bandwidth of HTTP server? Or maybe there is an issue with my testing method or local configuration?

Kirill
  • 7,580
  • 6
  • 44
  • 95
  • 2
    The way you compute the bandwidth is not optimal. A ticker is not a reliable measure of time. You should let the counter value increment and divide its value by the elapsed time to get the average ticks per seconds. Another problem is that you are missing a `p.Body.Close()` in your client. Not sure if it may affect performances, but it certainly affects memory management. – chmike Dec 14 '19 at 09:20
  • 1
    Starting 10000 go routines is not a good. Idea. Does your computer have 10000 cores ? They will execute in sequence and slow down their execution. You should start a small number of go routines and grow the number until you don't see a change in performance. – chmike Dec 14 '19 at 09:25
  • 3
    Try using an existing benchmark tool like [wrk](https://github.com/wg/wrk) so you only have to worry about improving the server. – Ry- Dec 14 '19 at 09:28
  • 2
    And for gods sake, use a proper setup. "Measuring" the performance of a server implementation with both the server and the client running on a development machine (which does all sorts of stuff in parallel and might well run into limits such as number of open files, etc) is like measuring your body temperature by putting your *own* hand on your forehead. Put the server onto the target platform, measure the number of requests for *your use case* it can reliably fulfill. This number minus a bit is your scaling threshold for horizontal scaling. Problem solved. – Markus W Mahlberg Dec 14 '19 at 09:39
  • All of above and `fmt.Println` may not happen immediately after receiving ticks. – leaf bebop Dec 14 '19 at 10:05
  • Thinking of it, the no files is WAY to low: You need 2 fds per connection, to start with. Access to the port and whatnot. Remember, ***everything*** is a file. – Markus W Mahlberg Dec 14 '19 at 10:22
  • have a look to https://stackoverflow.com/questions/33238518/what-could-happen-if-i-dont-close-response-body-in-golang –  Dec 14 '19 at 12:12
  • @Ry- thanks, I replaced custom go client with `wrk`, and now it shows about `380000` requests per second – Kirill Dec 14 '19 at 13:26

1 Answers1

1

The problem was in testing method:

  1. It's not correct to run client and server on the same machine, the target server should be located at dedicated host, the network between target and client should be fast enough
  2. Custom scripts for network testing is not an option: for simple cases wrk can be used, for more complex scenarios Jmetr or other frameworks

When I tested this server on dedicated host using wrk it shows 285900.73 RPS.

Kirill
  • 7,580
  • 6
  • 44
  • 95