Skip to content

Instantly share code, notes, and snippets.

@juanpabloaj
Last active October 23, 2020 18:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save juanpabloaj/bca2f410ccce77f2e7bde8640e23330c to your computer and use it in GitHub Desktop.
Save juanpabloaj/bca2f410ccce77f2e7bde8640e23330c to your computer and use it in GitHub Desktop.
go http service with concurrency

Build binaries

make build

Start services running in background or start these in another terminal.

PORT=8080 ./server &
PORT=5000 ./serverA &
PORT=6000 ./serverB &

Run load tests over the services

Case 1

ab -n 500000 -c 50 0.0.0.0:8080/withoutgoroutine
...
Percentage of the requests served within a certain time (ms)
  50%      3
  66%      4
  75%      4
  80%      4
  90%      5
  95%      7
  98%     10
  99%     11
 100%     38 (longest request)

Case 2

ab -n 500000 -c 50 0.0.0.0:8080/withgoroutine
...
Percentage of the requests served within a certain time (ms)
  50%      5
  66%      5
  75%      6
  80%      6
  90%      8
  95%     10
  98%     14
  99%     16
 100%     42 (longest request)

Case 3

ab -n 500000 -c 50 0.0.0.0:8080/withsleepygoroutine
Percentage of the requests served within a certain time (ms)
...
  50%      3
  66%      4
  75%      4
  80%      4
  90%      5
  95%      7
  98%     10
  99%     12
 100%     31 (longest request)
module github.com/juanpabloaj/http_server_concurrency
go 1.14
require github.com/gorilla/mux v1.8.0
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
package main
import (
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/gorilla/mux"
)
var clientA *http.Client
var clientB *http.Client
func httpGet(client *http.Client, requestString string) {
request, err := http.NewRequest("GET", requestString, nil)
if err != nil {
log.Printf("%v", err)
return
}
_, err = client.Do(request)
if err != nil {
log.Printf("[%v]", err)
}
}
func httpGetA() {
httpGet(clientA, "http://0.0.0.0:5000")
}
func httpGetB() {
httpGet(clientB, "http://0.0.0.0:6000")
}
func deafaultHandler(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(``))
if err != nil {
log.Printf("%v", err)
}
}
func withoutGoroutine(w http.ResponseWriter, r *http.Request) {
httpGetA()
_, err := w.Write([]byte(``))
if err != nil {
log.Printf("%v", err)
}
}
func withGoroutine(w http.ResponseWriter, r *http.Request) {
httpGetA()
_, err := w.Write([]byte(``))
if err != nil {
log.Printf("%v", err)
}
go httpGetB()
}
func withSleepyGoroutine(w http.ResponseWriter, r *http.Request) {
httpGetA()
_, err := w.Write([]byte(``))
if err != nil {
log.Printf("%v", err)
}
go func() {
time.Sleep(1 * time.Millisecond)
}()
}
func newHTTPClient() *http.Client {
idleTimeSeconds := 30
timeoutSeconds := 2
idlePerHost := 20
maxConnsPerHost := 20
tr := &http.Transport{
MaxIdleConnsPerHost: maxConnsPerHost,
MaxConnsPerHost: idlePerHost,
IdleConnTimeout: time.Duration(idleTimeSeconds) * time.Second,
DisableKeepAlives: false,
}
return &http.Client{
Transport: tr,
Timeout: time.Second * time.Duration(timeoutSeconds),
}
}
func main() {
clientA = newHTTPClient()
clientB = newHTTPClient()
port := "8080"
if envPort := os.Getenv("PORT"); envPort != "" {
port = envPort
}
router := mux.NewRouter()
router.HandleFunc("/", deafaultHandler)
router.HandleFunc("/withoutgoroutine", withoutGoroutine)
router.HandleFunc("/withgoroutine", withGoroutine)
router.HandleFunc("/withsleepygoroutine", withSleepyGoroutine)
listenAddress := fmt.Sprintf(":%s", port)
log.Printf("Listening on %s", listenAddress)
log.Fatal(http.ListenAndServe(listenAddress, router))
}
build:
go build -o server
go build -o serverA
go build -o serverB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment