I have this running with go run main.go, and then I opened 2 terminal instances and execute the following command in each of them:
for i in `seq 1 1000`; do
curl localhost:3000/status
done
Some requests eventually took a lot of time to process. I see that it happens sometimes in production (a request take more time than it should), and I was able to easily reproduce this with this recipe.
I actually found this with labstack/echo, so I first opened an issue there, but the behavior is the same with the example provided here without any frameworks.
Am I doing something wrong? How can I fix this?
For further info:
$ go version
go version go1.6 darwin/amd64
$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/carlos/Code/Go"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.6/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.6/libexec/pkg/tool/darwin_amd64"
GO15VENDOREXPERIMENT="1"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
CXX="clang++"
CGO_ENABLED="1"
Can you try your experiment with a real load balancer tool, wrk or rakyll/boom are good choices. Do not use ab, it is known to be broken and Go programmers do not consider the results from that tool reliable.
The problem isnβt with Curl per se. This is a fundamental property of tcp that when a connection is closed, and exiting the program will close the connection, the ip:port pair must go into TIME_WAIT and TIME_WAIT2 states before it can be safely reused. During that time the ip:port pair is unavailable to make another outgoing (or incoming if youβre benchmarking over the loopback) so the kernel must find another free ip:port pair, there are 65336 possible when connection over the loopback, not all are available.
It is possible to avoid most of the TIME_WAIT delay with careful coding on both the client and the server. This cannot happen if the client exits, which is what happens when curl is run in a loop in a shell script.