Skip to content

Commit bb7af24

Browse files
committed
The TCPKeepalive functionality should be determined by the Server.TCPKeepalive setting.
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. I 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 bb7af24

File tree

3 files changed

+132
-7
lines changed

3 files changed

+132
-7
lines changed

client.go

Lines changed: 75 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,16 @@ type Client struct {
315322

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

320337
// Get returns the status code and body of url.
@@ -527,6 +544,8 @@ func (c *Client) Do(req *Request, resp *Response) error {
527544
MaxConns: c.MaxConnsPerHost,
528545
MaxIdleConnDuration: c.MaxIdleConnDuration,
529546
MaxConnDuration: c.MaxConnDuration,
547+
TCPKeepalivePeriod: c.TCPKeepalivePeriod,
548+
TCPKeepalive: c.TCPKeepalive,
530549
MaxIdemponentCallAttempts: c.MaxIdemponentCallAttempts,
531550
ReadBufferSize: c.ReadBufferSize,
532551
WriteBufferSize: c.WriteBufferSize,
@@ -796,6 +815,13 @@ type HostClient struct {
796815
// By default will not waiting, return ErrNoFreeConns immediately
797816
MaxConnWaitTimeout time.Duration
798817

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

@@ -871,6 +897,16 @@ type HostClient struct {
871897
StreamResponseBody bool
872898

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

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

18901942
c.addrsLock.Lock()
18911943
n := len(c.addrs)
@@ -2326,6 +2378,12 @@ type pipelineConnClient struct {
23262378
WriteBufferSize int
23272379
ReadTimeout time.Duration
23282380
WriteTimeout time.Duration
2381+
// TCPKeepalivePeriod is the duration the connection needs to
2382+
// remain idle before TCP starts sending keepalive probes.
2383+
//
2384+
// This field is only effective when TCPKeepalive is set to true.
2385+
// A zero value indicates that the operating system's default setting will be used.
2386+
TCPKeepalivePeriod time.Duration
23292387

23302388
chLock sync.Mutex
23312389

@@ -2335,6 +2393,15 @@ type pipelineConnClient struct {
23352393
DisableHeaderNamesNormalizing bool
23362394
DisablePathNormalizing bool
23372395
IsTLS bool
2396+
// Whether to enable tcp keep-alive connections.
2397+
//
2398+
// Whether the operating system should send tcp keep-alive messages on the tcp connection.
2399+
//
2400+
// By default tcp keep-alive connections are disabled.
2401+
//
2402+
// This option is used only if default TCP dialer is used,
2403+
// i.e. if Dial is blank.
2404+
TCPKeepalive bool
23382405
}
23392406

23402407
type pipelineWork struct {
@@ -2666,6 +2733,14 @@ func (c *pipelineConnClient) init() {
26662733

26672734
func (c *pipelineConnClient) worker() error {
26682735
tlsConfig := c.cachedTLSConfig()
2736+
if c.TCPKeepalive && c.Dial == nil {
2737+
d := &TCPDialer{Concurrency: defaultDialer.Concurrency, TCPKeepalive: true, TCPKeepalivePeriod: c.TCPKeepalivePeriod}
2738+
if c.DialDualStack {
2739+
c.Dial = d.DialDualStack
2740+
} else {
2741+
c.Dial = d.Dial
2742+
}
2743+
}
26692744
conn, err := dialAddr(c.Addr, c.Dial, nil, c.DialDualStack, c.IsTLS, tlsConfig, 0, c.WriteTimeout)
26702745
if err != nil {
26712746
return err

server.go

Lines changed: 27 additions & 6 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.
@@ -334,6 +336,9 @@ type Server struct {
334336
// Whether the operating system should send tcp keep-alive messages on the tcp connection.
335337
//
336338
// By default tcp keep-alive connections are disabled.
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: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func DialDualStackTimeout(addr string, timeout time.Duration) (net.Conn, error)
117117
return defaultDialer.DialDualStackTimeout(addr, timeout)
118118
}
119119

120-
var defaultDialer = &TCPDialer{Concurrency: 1000}
120+
var defaultDialer = &TCPDialer{Concurrency: 1000, TCPKeepalive: false}
121121

122122
// Resolver represents interface of the tcp resolver.
123123
type Resolver interface {
@@ -159,10 +159,24 @@ 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 to enable tcp keep-alive connections.
175+
//
176+
// Whether the operating system should send tcp keep-alive messages on the tcp connection.
177+
//
178+
// By default tcp keep-alive connections are disabled.
179+
TCPKeepalive bool
166180
}
167181

168182
// Dial dials the given TCP addr using tcp4.
@@ -344,6 +358,9 @@ func (d *TCPDialer) tryDial(
344358
if d.LocalAddr != nil {
345359
dialer.LocalAddr = d.LocalAddr
346360
}
361+
// Overriding the default behavior of net.Dialer,
362+
// whether TCP keepalive is enabled is determined by Dialer.KeepAlive.
363+
dialer.KeepAlive = -1
347364

348365
ctx, cancelCtx := context.WithDeadline(context.Background(), deadline)
349366
defer cancelCtx()
@@ -354,6 +371,18 @@ func (d *TCPDialer) tryDial(
354371
}
355372
return nil, wrapDialWithUpstream(err, addr)
356373
}
374+
if tc, ok := conn.(*net.TCPConn); ok && d.TCPKeepalive {
375+
if err = tc.SetKeepAlive(d.TCPKeepalive); err != nil {
376+
_ = tc.Close()
377+
return nil, err
378+
}
379+
if d.TCPKeepalivePeriod > 0 {
380+
if err = tc.SetKeepAlivePeriod(d.TCPKeepalivePeriod); err != nil {
381+
_ = tc.Close()
382+
return nil, err
383+
}
384+
}
385+
}
357386
return conn, nil
358387
}
359388

0 commit comments

Comments
 (0)