Skip to content

Instantly share code, notes, and snippets.

@CullenShane
Created July 13, 2018 16:29
Show Gist options
  • Save CullenShane/7cdc4e9b24af4e17f6202607d5878ddc to your computer and use it in GitHub Desktop.
Save CullenShane/7cdc4e9b24af4e17f6202607d5878ddc to your computer and use it in GitHub Desktop.
Jank `HTTP` client for loading servers and measuring connection time.

Purpose

I created this so that we could measure how often certian machines were unable to process all incoming requests. The problem usually manifests itself with TCP SYN Retries, and in that case, you can see it on a well performing network by seeing connection times taking 1, 3 or 7 seconds as each SYN packet is resent.

Design

It was designed to be run inside a container so it can be easily sent between some old infrastructure and new infrastructure to easily measure the difference between them.

Use

Be sure to override the Environment variables that are set in the program to get it to perform per your environment. I found that with another Go HTTP server that these settings would easily overwhelm the networking subsystem on a single machine.

Compiling

Since this is built on the 'scratch' docker image, you must compile the Golang program with some special flags so that it it doesn't need to inspect the machine to setup some runtime settings. The command below will get everything working.

If you use standard go build and build an image you'll find some difficult to understand errors about file not found.

CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo

FROM scratch
ADD syn-client /syn-client
ENV CONNECTION_DELAY "10"
ENV CONCURRENT_CONNECTIONS "200"
ENV SERVER "localhost:5000"
CMD ["/syn-client"]
package main
import "os"
import "fmt"
import "strconv"
import "net"
import "sync/atomic"
import "bufio"
import "time"
import "sync"
var ops uint64
func main() {
connDelay, err := strconv.Atoi(os.Getenv("CONNECTION_DELAY"))
if err != nil {
// handle error
fmt.Println(err)
fmt.Println("Looks like CONNECTION_DELAY wasn't a number. :SADFACE:")
os.Exit(2)
}
concurrency, err := strconv.Atoi(os.Getenv("CONCURRENT_CONNECTIONS"))
if err != nil {
// handle error
fmt.Println(err)
fmt.Println("Looks like CONCURRENT_CONNECTIONS wasn't a number. :SADFACE:")
os.Exit(2)
}
serverAndPort, set := os.LookupEnv("SERVER")
if set == false {
fmt.Println("Looks like you didn't set SERVER, it should be like 'localhost:5000'")
os.Exit(2)
}
loopCount := 1
loops := 10
loopsStr, set := os.LookupEnv("LOOPS")
if set == false {
loopCount = 0
} else {
loops, err = strconv.Atoi(loopsStr)
if err != nil {
// handle error
fmt.Println(err)
fmt.Println("Looks like your number of loops wasn't a number, to go forever send '0'. :SADFACE:")
os.Exit(2)
}
}
if loops == 0 {
loopCount = 0
} else {
loops--
}
print("SENDING IT")
var wg sync.WaitGroup
wg.Add(concurrency)
for i := 0; i < concurrency; i++ {
print(".")
go func() {
defer wg.Done()
var conn net.Conn
var err error
for j := 0; j <= loops; j += loopCount {
err = nil
atomic.AddUint64(&ops, 1)
connStart := time.Now()
conn, err = net.Dial("tcp", serverAndPort)
if err != nil {
fmt.Printf(err.Error())
//time.Sleep(time.Millisecond * 499)
continue
}
connDuration := time.Since(connStart)
if connDuration > time.Second {
print("Connecting took too long! ")
print(connDuration / time.Millisecond)
println("ms")
}
conn.Write([]byte("GET /server.go HTTP/1.0\n"))
conn.Write([]byte("From: cullen@particle.io\n"))
conn.Write([]byte("User-Agent: Blerp/1.0\n"))
conn.Write([]byte("Content-Length: 0\n\n\n"))
_, _ = bufio.NewReader(conn).ReadString('\n')
_, _ = bufio.NewReader(conn).ReadString('\n')
_, _ = bufio.NewReader(conn).ReadString('\n')
_, _ = bufio.NewReader(conn).ReadString('\n')
conn.Close()
conn = nil
time.Sleep(time.Microsecond * time.Duration(connDelay))
}
}()
}
print(".")
wg.Wait()
println("SENT")
println("operations: ", ops)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment