mirror of https://github.com/grpc/grpc-go.git
client: always enable TCP keepalives with OS defaults (#6834)
This commit is contained in:
parent
c2398ced0e
commit
a03c7f1faa
|
|
@ -67,6 +67,7 @@ import (
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"google.golang.org/grpc/experimental"
|
"google.golang.org/grpc/experimental"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
@ -373,7 +374,7 @@ func makeClients(bf stats.Features) ([]testgrpc.BenchmarkServiceClient, func())
|
||||||
logger.Fatalf("Failed to listen: %v", err)
|
logger.Fatalf("Failed to listen: %v", err)
|
||||||
}
|
}
|
||||||
opts = append(opts, grpc.WithContextDialer(func(ctx context.Context, address string) (net.Conn, error) {
|
opts = append(opts, grpc.WithContextDialer(func(ctx context.Context, address string) (net.Conn, error) {
|
||||||
return nw.ContextDialer((&net.Dialer{KeepAlive: time.Duration(-1)}).DialContext)(ctx, "tcp", lis.Addr().String())
|
return nw.ContextDialer((internal.NetDialerWithTCPKeepalive().DialContext))(ctx, "tcp", lis.Addr().String())
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
lis = nw.Listener(lis)
|
lis = nw.Listener(lis)
|
||||||
|
|
|
||||||
|
|
@ -401,10 +401,12 @@ func WithTimeout(d time.Duration) DialOption {
|
||||||
// returned by f, gRPC checks the error's Temporary() method to decide if it
|
// returned by f, gRPC checks the error's Temporary() method to decide if it
|
||||||
// should try to reconnect to the network address.
|
// should try to reconnect to the network address.
|
||||||
//
|
//
|
||||||
// Note: As of Go 1.21, the standard library overrides the OS defaults for
|
// Note: All supported releases of Go (as of December 2023) override the OS
|
||||||
// TCP keepalive time and interval to 15s.
|
// defaults for TCP keepalive time and interval to 15s. To enable TCP keepalive
|
||||||
// To retain OS defaults, use a net.Dialer with the KeepAlive field set to a
|
// with OS defaults for keepalive time and interval, use a net.Dialer that sets
|
||||||
// negative value.
|
// the KeepAlive field to a negative value, and sets the SO_KEEPALIVE socket
|
||||||
|
// option to true from the Control field. For a concrete example of how to do
|
||||||
|
// this, see internal.NetDialerWithTCPKeepalive().
|
||||||
//
|
//
|
||||||
// For more information, please see [issue 23459] in the Go github repo.
|
// For more information, please see [issue 23459] in the Go github repo.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 gRPC authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetDialerWithTCPKeepalive returns a net.Dialer that enables TCP keepalives on
|
||||||
|
// the underlying connection with OS default values for keepalive parameters.
|
||||||
|
//
|
||||||
|
// TODO: Once https://github.com/golang/go/issues/62254 lands, and the
|
||||||
|
// appropriate Go version becomes less than our least supported Go version, we
|
||||||
|
// should look into using the new API to make things more straightforward.
|
||||||
|
func NetDialerWithTCPKeepalive() *net.Dialer {
|
||||||
|
return &net.Dialer{
|
||||||
|
// Setting a negative value here prevents the Go stdlib from overriding
|
||||||
|
// the values of TCP keepalive time and interval. It also prevents the
|
||||||
|
// Go stdlib from enabling TCP keepalives by default.
|
||||||
|
KeepAlive: time.Duration(-1),
|
||||||
|
// This method is called after the underlying network socket is created,
|
||||||
|
// but before dialing the socket (or calling its connect() method). The
|
||||||
|
// combination of unconditionally enabling TCP keepalives here, and
|
||||||
|
// disabling the overriding of TCP keepalive parameters by setting the
|
||||||
|
// KeepAlive field to a negative value above, results in OS defaults for
|
||||||
|
// the TCP keealive interval and time parameters.
|
||||||
|
Control: func(_, _ string, c syscall.RawConn) error {
|
||||||
|
return c.Control(func(fd uintptr) {
|
||||||
|
syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
icredentials "google.golang.org/grpc/internal/credentials"
|
icredentials "google.golang.org/grpc/internal/credentials"
|
||||||
"google.golang.org/grpc/internal/grpclog"
|
"google.golang.org/grpc/internal/grpclog"
|
||||||
|
|
@ -43,7 +44,7 @@ import (
|
||||||
"google.golang.org/grpc/internal/grpcutil"
|
"google.golang.org/grpc/internal/grpcutil"
|
||||||
imetadata "google.golang.org/grpc/internal/metadata"
|
imetadata "google.golang.org/grpc/internal/metadata"
|
||||||
istatus "google.golang.org/grpc/internal/status"
|
istatus "google.golang.org/grpc/internal/status"
|
||||||
"google.golang.org/grpc/internal/syscall"
|
isyscall "google.golang.org/grpc/internal/syscall"
|
||||||
"google.golang.org/grpc/internal/transport/networktype"
|
"google.golang.org/grpc/internal/transport/networktype"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
@ -176,9 +177,7 @@ func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error
|
||||||
if networkType == "tcp" && useProxy {
|
if networkType == "tcp" && useProxy {
|
||||||
return proxyDial(ctx, address, grpcUA)
|
return proxyDial(ctx, address, grpcUA)
|
||||||
}
|
}
|
||||||
// KeepAlive is set to a negative value to prevent Go's override of the TCP
|
return internal.NetDialerWithTCPKeepalive().DialContext(ctx, networkType, address)
|
||||||
// keepalive time and interval; retain the OS default.
|
|
||||||
return (&net.Dialer{KeepAlive: time.Duration(-1)}).DialContext(ctx, networkType, address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTemporary(err error) bool {
|
func isTemporary(err error) bool {
|
||||||
|
|
@ -264,7 +263,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
|
||||||
}
|
}
|
||||||
keepaliveEnabled := false
|
keepaliveEnabled := false
|
||||||
if kp.Time != infinity {
|
if kp.Time != infinity {
|
||||||
if err = syscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil {
|
if err = isyscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil {
|
||||||
return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err)
|
return nil, connectionErrorf(false, err, "transport: failed to set TCP_USER_TIMEOUT: %v", err)
|
||||||
}
|
}
|
||||||
keepaliveEnabled = true
|
keepaliveEnabled = true
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
|
"google.golang.org/grpc/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
const proxyAuthHeaderKey = "Proxy-Authorization"
|
const proxyAuthHeaderKey = "Proxy-Authorization"
|
||||||
|
|
@ -113,7 +114,7 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr stri
|
||||||
// proxyDial dials, connecting to a proxy first if necessary. Checks if a proxy
|
// proxyDial dials, connecting to a proxy first if necessary. Checks if a proxy
|
||||||
// is necessary, dials, does the HTTP CONNECT handshake, and returns the
|
// is necessary, dials, does the HTTP CONNECT handshake, and returns the
|
||||||
// connection.
|
// connection.
|
||||||
func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn, err error) {
|
func proxyDial(ctx context.Context, addr string, grpcUA string) (net.Conn, error) {
|
||||||
newAddr := addr
|
newAddr := addr
|
||||||
proxyURL, err := mapAddress(addr)
|
proxyURL, err := mapAddress(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -123,15 +124,15 @@ func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn,
|
||||||
newAddr = proxyURL.Host
|
newAddr = proxyURL.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err = (&net.Dialer{KeepAlive: time.Duration(-1)}).DialContext(ctx, "tcp", newAddr)
|
conn, err := internal.NetDialerWithTCPKeepalive().DialContext(ctx, "tcp", newAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
if proxyURL != nil {
|
if proxyURL == nil {
|
||||||
// proxy is disabled if proxyURL is nil.
|
// proxy is disabled if proxyURL is nil.
|
||||||
conn, err = doHTTPConnectHandshake(ctx, conn, addr, proxyURL, grpcUA)
|
return conn, err
|
||||||
}
|
}
|
||||||
return
|
return doHTTPConnectHandshake(ctx, conn, addr, proxyURL, grpcUA)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
||||||
|
|
|
||||||
19
server.go
19
server.go
|
|
@ -814,14 +814,17 @@ func (l *listenSocket) Close() error {
|
||||||
// this method returns.
|
// this method returns.
|
||||||
// Serve will return a non-nil error unless Stop or GracefulStop is called.
|
// Serve will return a non-nil error unless Stop or GracefulStop is called.
|
||||||
//
|
//
|
||||||
// Note: As of Go 1.21, the standard library overrides the OS defaults for
|
// Note: All supported releases of Go (as of December 2023) override the OS
|
||||||
// TCP keepalive time and interval to 15s.
|
// defaults for TCP keepalive time and interval to 15s. To enable TCP keepalive
|
||||||
// To retain OS defaults, pass a net.Listener created by calling the Listen method
|
// with OS defaults for keepalive time and interval, callers need to do the
|
||||||
// on a net.ListenConfig with the `KeepAlive` field set to a negative value.
|
// following two things:
|
||||||
//
|
// - pass a net.Listener created by calling the Listen method on a
|
||||||
// For more information, please see [issue 23459] in the Go github repo.
|
// net.ListenConfig with the `KeepAlive` field set to a negative value. This
|
||||||
//
|
// will result in the Go standard library not overriding OS defaults for TCP
|
||||||
// [issue 23459]: https://github.com/golang/go/issues/23459
|
// keepalive interval and time. But this will also result in the Go standard
|
||||||
|
// library not enabling TCP keepalives by default.
|
||||||
|
// - override the Accept method on the passed in net.Listener and set the
|
||||||
|
// SO_KEEPALIVE socket option to enable TCP keepalives, with OS defaults.
|
||||||
func (s *Server) Serve(lis net.Listener) error {
|
func (s *Server) Serve(lis net.Listener) error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.printf("serving")
|
s.printf("serving")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue