Skip to content

Commit 1389694

Browse files
committed
Add a workaround for HTTP/2 connection reuse
Sometimes we're seeing HTTP/2 connections be reused for up to ~16 minutes despite being completely defunct -- i.e. we're receiving no response from the remote end. We believe that this is in part a result of Go bug golang/go#59690 in which failed connections are not correctly discarded under certain circumstances. This commit enables the http2 transport's `ReadIdleTimeout` function, which will send an h2 "ping" frame on any connection that's been idle longer than the value of `ReadIdleTimeout`. If it doesn't hear back in `PingTimeout`, it will throw away the connection. This seems like a reasonable thing to leave on in general.
1 parent e251d3c commit 1389694

File tree

1 file changed

+28
-0
lines changed

1 file changed

+28
-0
lines changed

Diff for: httpclient/httpclient.go

+28
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
1515
"go.opentelemetry.io/otel/propagation"
16+
"golang.org/x/net/http2"
1617
)
1718

1819
const ConnectTimeout = 5 * time.Second
@@ -72,6 +73,7 @@ func DefaultPooledTransport() *http.Transport {
7273
ExpectContinueTimeout: 1 * time.Second,
7374
MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1,
7475
}
76+
configureHTTP2(transport)
7577
return transport
7678
}
7779

@@ -93,3 +95,29 @@ func DefaultPooledClient() *http.Client {
9395
Transport: DefaultPooledRoundTripper(),
9496
}
9597
}
98+
99+
func configureHTTP2(t *http.Transport) {
100+
h2t, err := http2.ConfigureTransports(t)
101+
if err != nil {
102+
// ConfigureTransports should only ever return an error if the transport
103+
// passed in has already been configured for http2, which shouldn't be
104+
// possible for us.
105+
panic(err)
106+
}
107+
108+
// Send a ping frame on any connection that's been idle for more than 10
109+
// seconds.
110+
//
111+
// The default is to never do this. We set it primarily as a workaround for
112+
//
113+
// https://github.com/golang/go/issues/59690
114+
//
115+
// where a connection that goes AWOL will not be correctly terminated and
116+
// removed from the connection pool under certain circumstances. Together
117+
// `ReadIdleTimeout` and `PingTimeout` should ensure that we remove defunct
118+
// connections in ~20 seconds.
119+
h2t.ReadIdleTimeout = 10 * time.Second
120+
// Give the other end 10 seconds to respond. If we don't hear back, we'll
121+
// close the connection.
122+
h2t.PingTimeout = 10 * time.Second
123+
}

0 commit comments

Comments
 (0)