Skip to content

Instantly share code, notes, and snippets.

@mborsz

mborsz/main.go Secret

Created March 16, 2023 09:53
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 mborsz/8f4dc894a6fa8fea8adb19a899dc16f3 to your computer and use it in GitHub Desktop.
Save mborsz/8f4dc894a6fa8fea8adb19a899dc16f3 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"context"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"sync"
"time"
"golang.org/x/net/http2"
)
const (
inflight = 1000
totalRequests = 20000
frameSize = 16 * 1024
)
func handlerLongRunning(w http.ResponseWriter, r *http.Request) {
for {
select {
case <-r.Context().Done():
log.Printf("long running finished, err=%v", r.Context().Err())
return
default:
}
now := []byte(time.Now().Format(time.RFC3339Nano) + "|")
data := bytes.Repeat(now, frameSize/len(now)+1)
data = data[:frameSize]
w.Write(data)
w.(http.Flusher).Flush()
time.Sleep(100 * time.Millisecond)
}
}
func handlerGet(w http.ResponseWriter, r *http.Request) {
// Just write 100M of zeros.
w.Write(make([]byte, 10*1024*1024))
w.(http.Flusher).Flush()
}
func dump(ctx context.Context, c *http.Client, url string) {
maxLatency := time.Duration(0)
log.Printf("starting url %s", url)
start := time.Now()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
log.Fatalf("failed to construct request: %v", err)
}
resp, err := c.Do(req)
if err != nil {
log.Fatalf("failed to get: %v", err)
}
defer resp.Body.Close()
buf := make([]byte, frameSize)
for {
n, err := resp.Body.Read(buf)
if err == io.EOF {
break
}
if err != nil {
log.Printf("got err=%v", err)
break
}
res := bytes.SplitN(buf, []byte{'|'}, 3)
if len(res) < 3 {
log.Printf("unexpected length of bytes. len(res)=%d, n=%d", len(res), n)
break
}
sent, err := time.Parse(time.RFC3339Nano, string(res[1]))
if err != nil {
log.Printf("failed to parse time: err=%v", err)
}
latency := time.Since(sent)
log.Printf("url=%v got %v, latency=%v", url, n, latency)
if latency > maxLatency {
maxLatency = latency
}
}
log.Printf("finished url %s, took %v, maxLatency=%v", url, time.Since(start), maxLatency)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/longrunning", handlerLongRunning)
mux.HandleFunc("/get", handlerGet)
ts := httptest.NewUnstartedServer(mux)
ts.EnableHTTP2 = true
if err := http2.ConfigureServer(ts.Config, &http2.Server{
MaxReadFrameSize: frameSize,
NewWriteScheduler: func() http2.WriteScheduler {
return http2.NewRandomWriteScheduler()
// return NewRoundRobinWriteScheduler(nil)
// return http2.NewPriorityWriteScheduler(nil)
},
}); err != nil {
log.Fatalf("failed ot configure server: %v", err)
}
ts.StartTLS()
defer ts.Close()
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
log.Printf("started url: %q", ts.URL)
go dump(ctx, ts.Client(), ts.URL+"/longrunning")
// Give the long running some time to start.
time.Sleep(1 * time.Second)
var wg sync.WaitGroup
wg.Add(inflight)
requests := make(chan int, totalRequests)
for i := 0; i < totalRequests; i++ {
requests <- i
}
close(requests)
for i := 0; i < inflight; i++ {
go func() {
for {
idx, ok := <-requests
if !ok {
break
}
func() {
start := time.Now()
resp, err := ts.Client().Get(ts.URL + "/get")
if err != nil {
log.Fatalf("[%d] Get err=%v", idx, err)
}
defer resp.Body.Close()
written, err := io.Copy(ioutil.Discard, resp.Body)
if err != nil {
if err != nil {
log.Fatalf("[%d] Copy err=%v", idx, err)
}
}
log.Printf("attempt %d read %d bytes and took %v", idx, written, time.Since(start))
}()
}
wg.Done()
}()
}
wg.Wait()
// Allow to deliver long running frames.
time.Sleep(1 * time.Second)
}
Inflight = 1000, totalRequests = 20000, frameSize = 16 * 1024, random
2023/03/16 09:04:21 finished url https://127.0.0.1:35941/longrunning, took 2m22.749233857s, maxLatency=22.541549601s
2023/03/16 09:16:08 finished url https://127.0.0.1:45509/longrunning, took 2m22.914708939s, maxLatency=18.330607707s
2023/03/16 09:20:35 finished url https://127.0.0.1:34635/longrunning, took 2m22.420850383s, maxLatency=16.384647093s
Inflight = 1000, totalRequests = 20000, frameSize = 16 * 1024, round robin
2023/03/16 09:24:15 finished url https://127.0.0.1:40443/longrunning, took 2m43.572479808s, maxLatency=1.695812761s
2023/03/16 09:28:08 finished url https://127.0.0.1:43495/longrunning, took 2m42.647369718s, maxLatency=736.103012ms
2023/03/16 09:31:49 finished url https://127.0.0.1:37509/longrunning, took 2m50.088901237s, maxLatency=1.515095342s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment