From 52c27e227afe9bd737bbe6842e4ebc3a6954f590 Mon Sep 17 00:00:00 2001 From: "Alex Ellis (VMware)" Date: Fri, 18 Jan 2019 17:50:43 +0000 Subject: [PATCH] Tune HTTP client for concurrency - 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. https://github.com/prometheus/prometheus/pull/3592 https://github.com/minio/minio/pull/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) --- gateway/handlers/forwarding_proxy.go | 21 +++++++++++++----- gateway/types/proxy_client.go | 33 ++++++++++++++++------------ 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/gateway/handlers/forwarding_proxy.go b/gateway/handlers/forwarding_proxy.go index 4e27fc3f..532694f4 100644 --- a/gateway/handlers/forwarding_proxy.go +++ b/gateway/handlers/forwarding_proxy.go @@ -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() @@ -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) + } + }() + } } @@ -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()) } diff --git a/gateway/types/proxy_client.go b/gateway/types/proxy_client.go index 8b40aaad..97bf0125 100644 --- a/gateway/types/proxy_client.go +++ b/gateway/types/proxy_client.go @@ -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