Last active
May 16, 2023 06:13
-
-
Save guanzo/84142f41ebee648bd08e894b7c8470ee to your computer and use it in GitHub Desktop.
go http1 vs http2 benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto/tls" | |
"flag" | |
"fmt" | |
"io/ioutil" | |
_ "io/ioutil" | |
"math/rand" | |
"net/http" | |
"net/http/httptrace" | |
"strconv" | |
"sync" | |
"time" | |
"github.com/montanaflynn/stats" | |
"golang.org/x/net/http2" | |
) | |
const ( | |
TIMEOUT_SEC = 60 | |
) | |
type Options struct { | |
NumRequests int | |
HttpVersion int | |
} | |
var http1Client = &http.Client{ | |
Timeout: time.Duration(TIMEOUT_SEC) * time.Second, | |
Transport: &http.Transport{ | |
MaxIdleConns: 1000, | |
MaxConnsPerHost: 1000, | |
MaxIdleConnsPerHost: 1000, | |
IdleConnTimeout: 90 * time.Second, | |
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, | |
}, | |
} | |
var http2Client = &http.Client{ | |
Timeout: time.Duration(TIMEOUT_SEC) * time.Second, | |
Transport: &http2.Transport{ | |
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, | |
DisableCompression: true, | |
AllowHTTP: true, | |
MaxReadFrameSize: 262144 * 4, // defaults to 16k | |
CountError: func(errType string) { | |
println(errType) | |
}, | |
}, | |
} | |
type RequestResult struct { | |
TTFB int | |
Status int | |
DurationMs int64 | |
ResponseSize int | |
RequestErr error | |
} | |
func sendRequest(httpVersion int) RequestResult { | |
start := time.Now() | |
var ttfb int64 | |
var requestErr error | |
var status int | |
var responseSize int | |
var client *http.Client | |
if httpVersion == 1 { | |
client = http1Client | |
} else { | |
client = http2Client | |
} | |
// file sizes | |
// 1 = 64k | |
// 2 = 256K | |
// 3 = 1MB | |
// 4 = 5MB | |
// 5 = 10MB | |
// 6 = 20MB | |
// 7 = 48MB | |
// | |
// tweak the min/max file number to change the min/max response size | |
min := 1 | |
max := 4 // exclusive | |
fileNum := rand.Intn(max - min) + min | |
testURL := "https://51.81.155.166/testfiles/" + strconv.Itoa(fileNum) | |
req, err := http.NewRequest("GET", testURL, nil) | |
if err != nil { | |
panic(err) | |
} | |
trace := &httptrace.ClientTrace{ | |
GotFirstResponseByte: func() { | |
ttfb = time.Since(start).Milliseconds() | |
}, | |
} | |
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) | |
resp, err := client.Do(req) | |
if err != nil { | |
requestErr = err | |
} | |
if resp != nil { | |
body, err := ioutil.ReadAll(resp.Body) | |
if err != nil { | |
fmt.Println("error", err) | |
} | |
responseSize = len(body) | |
status = resp.StatusCode | |
resp.Body.Close() | |
} | |
duration := int64(time.Since(start).Milliseconds()) | |
result := RequestResult{ | |
int(ttfb), | |
status, | |
duration, | |
responseSize, | |
requestErr, | |
} | |
return result | |
} | |
func benchmark (numRequests int, httpVersion int) { | |
var wg sync.WaitGroup | |
var mutex sync.Mutex | |
var results []RequestResult | |
var ttfbList []float64 | |
start := time.Now() | |
count := float32(0) | |
numBytes := 0 | |
for i := 0; i < numRequests; i++ { | |
i++ | |
count++ | |
wg.Add(1) | |
go func() { | |
defer wg.Done() | |
result := sendRequest(httpVersion) | |
mutex.Lock() | |
results = append(results, result) | |
ttfbList = append(ttfbList, float64(result.TTFB)) | |
mutex.Unlock() | |
progress := float64(i) / float64(numRequests) * 100.0 | |
numBytes += result.ResponseSize | |
numMB := float32(numBytes) / float32(1048576) | |
durationSec := (int(time.Since(start).Seconds())) | |
reqPerSec := float32(0) | |
bandwidth := float32(0) | |
if durationSec > 0 { | |
bandwidth = numMB / float32(time.Since(start).Seconds()) | |
reqPerSec = count / float32(durationSec) | |
} | |
fmt.Printf("%d, %.2f%%, %.2f r/s, %.2f mb/s, %+v\n", i, progress, reqPerSec, bandwidth, result) | |
}() | |
time.Sleep(time.Millisecond * 10) | |
} | |
wg.Wait() | |
fmt.Println( | |
"ttfb", | |
calcPercentile(ttfbList, 50), | |
calcPercentile(ttfbList, 90), | |
calcPercentile(ttfbList, 95), | |
calcPercentile(ttfbList, 99), | |
) | |
} | |
func calcPercentile (values []float64, percent float64) float64 { | |
result, _ := stats.Percentile(values, percent) | |
return result | |
} | |
func parseOptions() *Options { | |
var numLogs, httpVersion int | |
flag.IntVar(&numLogs, "c", 5000, "number of requests") | |
flag.IntVar(&httpVersion, "http", 1, "HTTP version to use") | |
flag.Parse() | |
return &Options{ | |
NumRequests: numLogs, | |
HttpVersion: httpVersion, | |
} | |
} | |
// go run cmd/bench/main.go -http 2 | |
func main() { | |
rand.Seed(time.Now().UnixNano()) | |
opts := parseOptions() | |
benchmark(opts.NumRequests, opts.HttpVersion) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment