Skip to content

Commit

Permalink
agent: allow to set SO_REUSEPORT on listening socket
Browse files Browse the repository at this point in the history
Introduce Option.SocketReuseAddrAndPort which, if set, will lead to the
SO_REUSEPORT socket option being set on the listening socket on
Unix-like OSes. This also sets SO_REUSEADDR which is already the default
in net.Listen (see net.setDefaultSockopts).

Setting these options increases the chance to re-bind() to the same
address and port upon agent restart if Options.Addr is set.

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
  • Loading branch information
tklauser committed Dec 9, 2020
1 parent b55933e commit 268f11e
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 5 deletions.
15 changes: 13 additions & 2 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package agent

import (
"bufio"
"context"
"encoding/binary"
"fmt"
"io"
Expand Down Expand Up @@ -55,6 +56,13 @@ type Options struct {
// can call Close before shutting down.
// Optional.
ShutdownCleanup bool

// ReuseSocketAddrAndPort determines whether the SO_REUSEADDR and
// SO_REUSEADDR socket options should be set on the listening socket of
// the agent. This option is only effective on unix-like OSes and if
// Addr is set to a fixed host:port.
// Optional.
ReuseSocketAddrAndPort bool
}

// Listen starts the gops agent on a host process. Once agent started, users
Expand Down Expand Up @@ -96,11 +104,14 @@ func Listen(opts Options) error {
if addr == "" {
addr = defaultAddr
}
ln, err := net.Listen("tcp", addr)
var lc net.ListenConfig
if opts.ReuseSocketAddrAndPort {
lc.Control = setsockoptReuseAddrAndPort
}
listener, err = lc.Listen(context.Background(), "tcp", addr)
if err != nil {
return err
}
listener = ln
port := listener.Addr().(*net.TCPAddr).Port
portfile = fmt.Sprintf("%s/%d", gopsdir, os.Getpid())
err = ioutil.WriteFile(portfile, []byte(strconv.Itoa(port)), os.ModePerm)
Expand Down
11 changes: 11 additions & 0 deletions agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ func TestAgentListenMultipleClose(t *testing.T) {
Close()
}

func TestAgentListenReuseAddrAndPort(t *testing.T) {
err := Listen(Options{
Addr: "127.0.0.1:50000",
ReuseSocketAddrAndPort: true,
})
if err != nil {
t.Fatal(err)
}
Close()
}

func TestFormatBytes(t *testing.T) {
tests := []struct {
val uint64
Expand Down
36 changes: 36 additions & 0 deletions agent/sockopt_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !js,!plan9,!windows

package agent

import (
"syscall"

"golang.org/x/sys/unix"
)

// setsockoptReuseAddrAndPort sets the SO_REUSEADDR and SO_REUSEPORT socket
// options on c's underlying socket in order to increase the chance to re-bind()
// to the same address and port upon agent restart.
func setsockoptReuseAddrAndPort(network, address string, c syscall.RawConn) error {
var soerr error
if err := c.Control(func(su uintptr) {
sock := int(su)
// Allow reuse of recently-used addresses. This socket option is
// set by default on listeners in Go's net package, see
// net.setDefaultSockopts.
soerr = unix.SetsockoptInt(sock, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if soerr != nil {
return
}
// Allow reuse of recently-used ports. This gives the agent a
// better chance to re-bind upon restarts.
soerr = unix.SetsockoptInt(sock, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
}); err != nil {
return err
}
return soerr
}
13 changes: 13 additions & 0 deletions agent/sockopt_unsupported.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build js,wasm plan9 windows

package agent

import "syscall"

func setsockoptReuseAddrAndPort(network, address string, c syscall.RawConn) error {
return nil
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ require (
github.com/shirou/gopsutil v2.20.4+incompatible
github.com/stretchr/testify v1.3.0 // indirect
github.com/xlab/treeprint v1.0.0
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 // indirect
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d
rsc.io/goversion v1.2.0
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/xlab/treeprint v1.0.0 h1:J0TkWtiuYgtdlrkkrDLISYBQ92M+X5m4LrIIMKrbDTs=
github.com/xlab/treeprint v1.0.0/go.mod h1:IoImgRak9i3zJyuxOKUP1v4UZd1tMoKkq/Cimt1uhCg=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d h1:MiWWjyhUzZ+jvhZvloX6ZrUsdEghn8a64Upd8EMHglE=
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
rsc.io/goversion v1.2.0 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w=
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=

0 comments on commit 268f11e

Please sign in to comment.