Skip to content

Commit

Permalink
Tune HTTP client for concurrency
Browse files Browse the repository at this point in the history
- due to what appears to be a frequent issue with the Go HTTP
client some tweaks were needed to the HTTP client used for
reverse proxying to prevent CoreDNS from rejecting connections.

The following PRs / commits implement similar changes in
Prometheus and Minio.

prometheus/prometheus#3592
minio/minio#5860

Under a 3-node (1-master) kubeadm cluster running on bare
metal with Ubuntu 18.04 I was able to send 100k requests
with 1000 being concurrent with no errors being returned
by hey.

```
hey -n 100000 -c 1000 -m=POST -d="hi" \
  http://192.168.0.26:31112/function/go-echo
```

The go-echo function is based upon the golang-http
template in the function store using the of-watchdog.

Signed-off-by: Alex Ellis (VMware) <[email protected]>
  • Loading branch information
alexellis committed Feb 4, 2019
1 parent 2269d0d commit 52c27e2
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 20 deletions.
21 changes: 15 additions & 6 deletions gateway/handlers/forwarding_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ type URLPathTransformer interface {

// MakeForwardingProxyHandler create a handler which forwards HTTP requests
func MakeForwardingProxyHandler(proxy *types.HTTPClientReverseProxy, notifiers []HTTPNotifier, baseURLResolver BaseURLResolver, urlPathTransformer URLPathTransformer) http.HandlerFunc {

writeRequestURI := false
if _, exists := os.LookupEnv("write_request_uri"); exists {
writeRequestURI = exists
}

return func(w http.ResponseWriter, r *http.Request) {
baseURL := baseURLResolver.Resolve(r)
originalURL := r.URL.String()
Expand All @@ -48,16 +54,19 @@ func MakeForwardingProxyHandler(proxy *types.HTTPClientReverseProxy, notifiers [

start := time.Now()

statusCode, err := forwardRequest(w, r, proxy.Client, baseURL, requestURL, proxy.Timeout)
statusCode, err := forwardRequest(w, r, proxy.Client, baseURL, requestURL, proxy.Timeout, writeRequestURI)

seconds := time.Since(start)
if err != nil {
log.Printf("error with upstream request to: %s, %s\n", requestURL, err.Error())
}

for _, notifier := range notifiers {
notifier.Notify(r.Method, requestURL, originalURL, statusCode, seconds)
}
defer func() {
for _, notifier := range notifiers {
notifier.Notify(r.Method, requestURL, originalURL, statusCode, seconds)
}
}()

}
}

Expand Down Expand Up @@ -86,14 +95,14 @@ func buildUpstreamRequest(r *http.Request, baseURL string, requestURL string) *h
return upstreamReq
}

func forwardRequest(w http.ResponseWriter, r *http.Request, proxyClient *http.Client, baseURL string, requestURL string, timeout time.Duration) (int, error) {
func forwardRequest(w http.ResponseWriter, r *http.Request, proxyClient *http.Client, baseURL string, requestURL string, timeout time.Duration, writeRequestURI bool) (int, error) {

upstreamReq := buildUpstreamRequest(r, baseURL, requestURL)
if upstreamReq.Body != nil {
defer upstreamReq.Body.Close()
}

if _, exists := os.LookupEnv("write_request_uri"); exists {
if writeRequestURI {
log.Printf("forwardRequest: %s %s\n", upstreamReq.Host, upstreamReq.URL.String())
}

Expand Down
33 changes: 19 additions & 14 deletions gateway/types/proxy_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,25 @@ func NewHTTPClientReverseProxy(baseURL *url.URL, timeout time.Duration) *HTTPCli
Timeout: timeout,
}

h.Client = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: timeout,
KeepAlive: 1 * time.Second,
}).DialContext,
IdleConnTimeout: 120 * time.Millisecond,
ExpectContinueTimeout: 1500 * time.Millisecond,
},
Timeout: timeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
h.Client = http.DefaultClient
h.Timeout = timeout
h.Client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

// Taken from http.DefaultTransport in Go 1.11
h.Client.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: timeout,
KeepAlive: timeout,
DualStack: true,
}).DialContext,
MaxIdleConns: 20000, // Overriden via https://github.com/errordeveloper/prometheus/commit/1f74477646aea93bebb7c098affa8e05132abb0c
MaxIdleConnsPerHost: 1024, // Overriden via https://github.com/minio/minio/pull/5860
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}

return &h
Expand Down

0 comments on commit 52c27e2

Please sign in to comment.