Skip to content

Commit 083dd1c

Browse files
committed
TCPKeepalive can be configured on both the server and client sides.
Currently, when Server.TCPKeepalive is set to false, if the net.Listener is created by the package itself, it does not override the default behavior of net.ListenConfig, which keeps TCP keepalive enabled. Also added TCPKeepalive and TCPKeepalivePeriod fields to the client to make its behavior consistent with the server. The goal of this update is to ensure that whether TCP keepalive is enabled is entirely controlled by the configuration fields provided by the package when net.TCPConn is created by the package itself.
1 parent d29a2b9 commit 083dd1c

File tree

3 files changed

+129
-9
lines changed

3 files changed

+129
-9
lines changed

client.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,13 @@ type Client struct {
228228
// By default connection duration is unlimited.
229229
MaxConnDuration time.Duration
230230

231+
// TCPKeepalivePeriod is the duration the connection needs to
232+
// remain idle before TCP starts sending keepalive probes.
233+
//
234+
// This field is only effective when TCPKeepalive is set to true.
235+
// A zero value indicates that the operating system's default setting will be used.
236+
TCPKeepalivePeriod time.Duration
237+
231238
// Maximum number of attempts for idempotent calls.
232239
//
233240
// DefaultMaxIdemponentCallAttempts is used if not set.
@@ -315,6 +322,14 @@ type Client struct {
315322

316323
// StreamResponseBody enables response body streaming.
317324
StreamResponseBody bool
325+
326+
// Whether the operating system should send tcp keep-alive messages on the tcp connection.
327+
//
328+
// By default tcp keep-alive connections are disabled.
329+
//
330+
// This option is used only if default TCP dialer is used,
331+
// i.e. if Dial and DialTimeout are blank.
332+
TCPKeepalive bool
318333
}
319334

320335
// Get returns the status code and body of url.
@@ -527,6 +542,8 @@ func (c *Client) Do(req *Request, resp *Response) error {
527542
MaxConns: c.MaxConnsPerHost,
528543
MaxIdleConnDuration: c.MaxIdleConnDuration,
529544
MaxConnDuration: c.MaxConnDuration,
545+
TCPKeepalivePeriod: c.TCPKeepalivePeriod,
546+
TCPKeepalive: c.TCPKeepalive,
530547
MaxIdemponentCallAttempts: c.MaxIdemponentCallAttempts,
531548
ReadBufferSize: c.ReadBufferSize,
532549
WriteBufferSize: c.WriteBufferSize,
@@ -796,6 +813,13 @@ type HostClient struct {
796813
// By default will not waiting, return ErrNoFreeConns immediately
797814
MaxConnWaitTimeout time.Duration
798815

816+
// TCPKeepalivePeriod is the duration the connection needs to
817+
// remain idle before TCP starts sending keepalive probes.
818+
//
819+
// This field is only effective when TCPKeepalive is set to true.
820+
// A zero value indicates that the operating system's default setting will be used.
821+
TCPKeepalivePeriod time.Duration
822+
799823
// Connection pool strategy. Can be either LIFO or FIFO (default).
800824
ConnPoolStrategy ConnPoolStrategyType
801825

@@ -871,6 +895,16 @@ type HostClient struct {
871895
StreamResponseBody bool
872896

873897
connsCleanerRun bool
898+
899+
// Whether to enable tcp keep-alive connections.
900+
//
901+
// Whether the operating system should send tcp keep-alive messages on the tcp connection.
902+
//
903+
// By default tcp keep-alive connections are disabled.
904+
//
905+
// This option is used only if default TCP dialer is used,
906+
// i.e. if Dial and DialTimeout are blank.
907+
TCPKeepalive bool
874908
}
875909

876910
type clientConn struct {
@@ -1886,6 +1920,22 @@ func (c *HostClient) dialHostHard(dialTimeout time.Duration) (conn net.Conn, err
18861920
// use dialTimeout to control the timeout of each dial. It does not work if dialTimeout is 0 or if
18871921
// c.DialTimeout has not been set and c.Dial has been set.
18881922
// attempt to dial all the available hosts before giving up.
1923+
if c.Dial == nil && c.DialTimeout == nil && c.TCPKeepalive {
1924+
d := &TCPDialer{Concurrency: defaultDialer.Concurrency, TCPKeepalive: true, TCPKeepalivePeriod: c.TCPKeepalivePeriod}
1925+
if dialTimeout > 0 {
1926+
if c.DialDualStack {
1927+
c.DialTimeout = d.DialDualStackTimeout
1928+
} else {
1929+
c.DialTimeout = d.DialTimeout
1930+
}
1931+
} else {
1932+
if c.DialDualStack {
1933+
c.Dial = d.DialDualStack
1934+
} else {
1935+
c.Dial = d.Dial
1936+
}
1937+
}
1938+
}
18891939

18901940
c.addrsLock.Lock()
18911941
n := len(c.addrs)
@@ -2326,6 +2376,12 @@ type pipelineConnClient struct {
23262376
WriteBufferSize int
23272377
ReadTimeout time.Duration
23282378
WriteTimeout time.Duration
2379+
// TCPKeepalivePeriod is the duration the connection needs to
2380+
// remain idle before TCP starts sending keepalive probes.
2381+
//
2382+
// This field is only effective when TCPKeepalive is set to true.
2383+
// A zero value indicates that the operating system's default setting will be used.
2384+
TCPKeepalivePeriod time.Duration
23292385

23302386
chLock sync.Mutex
23312387

@@ -2335,6 +2391,14 @@ type pipelineConnClient struct {
23352391
DisableHeaderNamesNormalizing bool
23362392
DisablePathNormalizing bool
23372393
IsTLS bool
2394+
2395+
// Whether the operating system should send tcp keep-alive messages on the tcp connection.
2396+
//
2397+
// By default tcp keep-alive connections are disabled.
2398+
//
2399+
// This option is used only if default TCP dialer is used,
2400+
// i.e. if Dial is blank.
2401+
TCPKeepalive bool
23382402
}
23392403

23402404
type pipelineWork struct {
@@ -2666,6 +2730,14 @@ func (c *pipelineConnClient) init() {
26662730

26672731
func (c *pipelineConnClient) worker() error {
26682732
tlsConfig := c.cachedTLSConfig()
2733+
if c.TCPKeepalive && c.Dial == nil {
2734+
d := &TCPDialer{Concurrency: defaultDialer.Concurrency, TCPKeepalive: true, TCPKeepalivePeriod: c.TCPKeepalivePeriod}
2735+
if c.DialDualStack {
2736+
c.Dial = d.DialDualStack
2737+
} else {
2738+
c.Dial = d.Dial
2739+
}
2740+
}
26692741
conn, err := dialAddr(c.Addr, c.Dial, nil, c.DialDualStack, c.IsTLS, tlsConfig, 0, c.WriteTimeout)
26702742
if err != nil {
26712743
return err

server.go

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,11 @@ type Server struct {
294294
// worker pool of the Server. Idle workers beyond this time will be cleared.
295295
MaxIdleWorkerDuration time.Duration
296296

297-
// Period between tcp keep-alive messages.
297+
// TCPKeepalivePeriod is the duration the connection needs to
298+
// remain idle before TCP starts sending keepalive probes.
298299
//
299-
// TCP keep-alive period is determined by operation system by default.
300+
// This field is only effective when TCPKeepalive is set to true.
301+
// A zero value indicates that the operating system's default setting will be used.
300302
TCPKeepalivePeriod time.Duration
301303

302304
// Maximum request body size.
@@ -329,11 +331,14 @@ type Server struct {
329331
// By default keep-alive connections are enabled.
330332
DisableKeepalive bool
331333

332-
// Whether to enable tcp keep-alive connections.
333-
//
334334
// Whether the operating system should send tcp keep-alive messages on the tcp connection.
335335
//
336-
// By default tcp keep-alive connections are disabled.
336+
// When this field is set to false, it is only effective if the net.Listener
337+
// is created by this package itself. i.e. listened by ListenAndServe, ListenAndServeUNIX,
338+
// ListenAndServeTLS and ListenAndServeTLSEmbed method.
339+
//
340+
// If you have created a net.Listener yourself and configured TCP keepalive,
341+
// this field should be set to false to avoid redundant additional system calls.
337342
TCPKeepalive bool
338343

339344
// Aggressively reduces memory usage at the cost of higher CPU usage
@@ -1623,7 +1628,7 @@ func (s *Server) getNextProto(c net.Conn) (proto string, err error) {
16231628
//
16241629
// Accepted connections are configured to enable TCP keep-alives.
16251630
func (s *Server) ListenAndServe(addr string) error {
1626-
ln, err := net.Listen("tcp4", addr)
1631+
ln, err := createTCPListener("tcp4", addr)
16271632
if err != nil {
16281633
return err
16291634
}
@@ -1639,7 +1644,7 @@ func (s *Server) ListenAndServeUNIX(addr string, mode os.FileMode) error {
16391644
if err := os.Remove(addr); err != nil && !os.IsNotExist(err) {
16401645
return fmt.Errorf("unexpected error when trying to remove unix socket file %q: %w", addr, err)
16411646
}
1642-
ln, err := net.Listen("unix", addr)
1647+
ln, err := createTCPListener("unix", addr)
16431648
if err != nil {
16441649
return err
16451650
}
@@ -1661,7 +1666,7 @@ func (s *Server) ListenAndServeUNIX(addr string, mode os.FileMode) error {
16611666
//
16621667
// Accepted connections are configured to enable TCP keep-alives.
16631668
func (s *Server) ListenAndServeTLS(addr, certFile, keyFile string) error {
1664-
ln, err := net.Listen("tcp4", addr)
1669+
ln, err := createTCPListener("tcp4", addr)
16651670
if err != nil {
16661671
return err
16671672
}
@@ -1680,7 +1685,7 @@ func (s *Server) ListenAndServeTLS(addr, certFile, keyFile string) error {
16801685
//
16811686
// Accepted connections are configured to enable TCP keep-alives.
16821687
func (s *Server) ListenAndServeTLSEmbed(addr string, certData, keyData []byte) error {
1683-
ln, err := net.Listen("tcp4", addr)
1688+
ln, err := createTCPListener("tcp4", addr)
16841689
if err != nil {
16851690
return err
16861691
}
@@ -2983,3 +2988,19 @@ var stateName = map[ConnState]string{
29832988
func (c ConnState) String() string {
29842989
return stateName[c]
29852990
}
2991+
2992+
// If the net.Listener is created by us, we override the default TCP
2993+
// keepalive behavior of net.ListenConfig, so that whether TCP keepalive is
2994+
// enabled is entirely determined by Server.KeepAlive.
2995+
//
2996+
// In the implementation of the net package, TCPListener
2997+
// by default always enables KeepAlive for new connections.
2998+
func createTCPListener(network, addr string) (ln net.Listener, err error) {
2999+
var lc net.ListenConfig
3000+
// Overriding the default TCP keepalive behavior of
3001+
// net.ListenConfig, whether TCP keepalive is enabled
3002+
// is determined by Server.KeepAlive.
3003+
lc.KeepAlive = -1
3004+
ln, err = lc.Listen(context.Background(), network, addr)
3005+
return
3006+
}

tcpdialer.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,22 @@ type TCPDialer struct {
159159
// DNSCacheDuration may be used to override the default DNS cache duration (DefaultDNSCacheDuration)
160160
DNSCacheDuration time.Duration
161161

162+
// TCPKeepalivePeriod is the duration the connection needs to
163+
// remain idle before TCP starts sending keepalive probes.
164+
//
165+
// This field is only effective when TCPKeepalive is set to true.
166+
// A zero value indicates that the operating system's default setting will be used.
167+
TCPKeepalivePeriod time.Duration
168+
162169
once sync.Once
163170

164171
// DisableDNSResolution may be used to disable DNS resolution
165172
DisableDNSResolution bool
173+
174+
// Whether the operating system should send tcp keep-alive messages on the tcp connection.
175+
//
176+
// By default tcp keep-alive connections are disabled.
177+
TCPKeepalive bool
166178
}
167179

168180
// Dial dials the given TCP addr using tcp4.
@@ -344,6 +356,9 @@ func (d *TCPDialer) tryDial(
344356
if d.LocalAddr != nil {
345357
dialer.LocalAddr = d.LocalAddr
346358
}
359+
// Overriding the default behavior of net.Dialer,
360+
// whether TCP keepalive is enabled is determined by Dialer.KeepAlive.
361+
dialer.KeepAlive = -1
347362

348363
ctx, cancelCtx := context.WithDeadline(context.Background(), deadline)
349364
defer cancelCtx()
@@ -354,6 +369,18 @@ func (d *TCPDialer) tryDial(
354369
}
355370
return nil, wrapDialWithUpstream(err, addr)
356371
}
372+
if tc, ok := conn.(*net.TCPConn); ok && d.TCPKeepalive {
373+
if err = tc.SetKeepAlive(d.TCPKeepalive); err != nil {
374+
_ = tc.Close()
375+
return nil, err
376+
}
377+
if d.TCPKeepalivePeriod > 0 {
378+
if err = tc.SetKeepAlivePeriod(d.TCPKeepalivePeriod); err != nil {
379+
_ = tc.Close()
380+
return nil, err
381+
}
382+
}
383+
}
357384
return conn, nil
358385
}
359386

0 commit comments

Comments
 (0)