transport/http: update default transport settings (#496)
* transport/http: update default transport settings
Updates the default HTTP transport to use a larger value for
MaxIdleConnsPerHost. This improves performance under high load
for the GCS client.
diff --git a/transport/http/default_transport_go113.go b/transport/http/default_transport_go113.go
new file mode 100644
index 0000000..924f270
--- /dev/null
+++ b/transport/http/default_transport_go113.go
@@ -0,0 +1,20 @@
+// Copyright 2020 Google LLC.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.13
+
+package http
+
+import "net/http"
+
+// clonedTransport returns the given RoundTripper as a cloned *http.Transport.
+// It returns nil if the RoundTripper can't be cloned or coerced to
+// *http.Transport.
+func clonedTransport(rt http.RoundTripper) *http.Transport {
+ t, ok := rt.(*http.Transport)
+ if !ok {
+ return nil
+ }
+ return t.Clone()
+}
diff --git a/transport/http/default_transport_not_go113.go b/transport/http/default_transport_not_go113.go
new file mode 100644
index 0000000..3cb16c6
--- /dev/null
+++ b/transport/http/default_transport_not_go113.go
@@ -0,0 +1,15 @@
+// Copyright 2020 Google LLC.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.13
+
+package http
+
+import "net/http"
+
+// clonedTransport returns the given RoundTripper as a cloned *http.Transport.
+// For versions of Go <1.13, this is not supported, so return nil.
+func clonedTransport(rt http.RoundTripper) *http.Transport {
+ return nil
+}
diff --git a/transport/http/dial.go b/transport/http/dial.go
index c8d79b8..96cf2e1 100644
--- a/transport/http/dial.go
+++ b/transport/http/dial.go
@@ -11,10 +11,13 @@
"context"
"crypto/tls"
"errors"
+ "net"
"net/http"
"net/url"
"os"
"strings"
+ "sync"
+ "time"
"go.opencensus.io/plugin/ochttp"
"golang.org/x/oauth2"
@@ -162,23 +165,57 @@
// defaultBaseTransport returns the base HTTP transport.
// On App Engine, this is urlfetch.Transport.
-// If TLSCertificate is available, return a custom Transport with TLSClientConfig.
-// Otherwise, return http.DefaultTransport.
+// Otherwise, use a default transport, taking most defaults from
+// http.DefaultTransport.
+// If TLSCertificate is available, set TLSClientConfig as well.
func defaultBaseTransport(ctx context.Context, clientCertSource cert.Source) http.RoundTripper {
if appengineUrlfetchHook != nil {
return appengineUrlfetchHook(ctx)
}
+ // Copy http.DefaultTransport except for MaxIdleConnsPerHost setting,
+ // which is increased due to reported performance issues under load in the GCS
+ // client. Transport.Clone is only available in Go 1.13 and up.
+ trans := clonedTransport(http.DefaultTransport)
+ if trans == nil {
+ trans = fallbackBaseTransport()
+ }
+ trans.MaxIdleConnsPerHost = 100
+
if clientCertSource != nil {
- // TODO (cbro): copy default transport settings from http.DefaultTransport
- return &http.Transport{
- TLSClientConfig: &tls.Config{
- GetClientCertificate: clientCertSource,
- },
+ trans.TLSClientConfig = &tls.Config{
+ GetClientCertificate: clientCertSource,
}
}
- return http.DefaultTransport
+ return trans
+}
+
+var fallback struct {
+ *http.Transport
+ sync.Once
+}
+
+// fallbackBaseTransport is used in <go1.13 as well as in the rare case if
+// http.DefaultTransport has been reassigned something that's not a
+// *http.Transport.
+func fallbackBaseTransport() *http.Transport {
+ fallback.Do(func() {
+ fallback.Transport = &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ DualStack: true,
+ }).DialContext,
+ MaxIdleConns: 100,
+ MaxIdleConnsPerHost: 100,
+ IdleConnTimeout: 90 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 1 * time.Second,
+ }
+ })
+ return fallback.Transport
}
func addOCTransport(trans http.RoundTripper, settings *internal.DialSettings) http.RoundTripper {