switch reuseport to github.com/valyala/tcplisten

This commit is contained in:
Aliaksandr Valialkin
2016-09-13 18:52:22 +03:00
parent 4f66eb3fbb
commit 95be7a5849
3 changed files with 13 additions and 147 deletions
+13 -99
View File
@@ -9,11 +9,10 @@
package reuseport
import (
"errors"
"fmt"
"github.com/valyala/tcplisten"
"net"
"os"
"syscall"
"strings"
)
// ErrNoReusePort is returned if the OS doesn't support SO_REUSEPORT.
@@ -36,107 +35,22 @@ func (e *ErrNoReusePort) Error() string {
//
// - TCP_FASTOPEN. See https://lwn.net/Articles/508865/ for details.
//
// Use https://github.com/valyala/tcplisten if you want customizing
// these options.
//
// Only tcp4 and tcp6 networks are supported.
//
// ErrNoReusePort error is returned if the system doesn't support SO_REUSEPORT.
func Listen(network, addr string) (net.Listener, error) {
sa, soType, err := getSockaddr(network, addr)
if err != nil {
return nil, err
ln, err := cfg.NewListener(network, addr)
if err != nil && strings.Contains(err.Error(), "SO_REUSEPORT") {
return nil, &ErrNoReusePort{err}
}
syscall.ForkLock.RLock()
fd, err := syscall.Socket(soType, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if err == nil {
syscall.CloseOnExec(fd)
}
syscall.ForkLock.RUnlock()
if err != nil {
return nil, err
}
if err = fdSetup(fd, sa, addr); err != nil {
syscall.Close(fd)
return nil, err
}
name := fmt.Sprintf("reuseport.%d.%s.%s", os.Getpid(), network, addr)
file := os.NewFile(uintptr(fd), name)
ln, err := net.FileListener(file)
if err != nil {
file.Close()
return nil, err
}
if err = file.Close(); err != nil {
ln.Close()
return nil, err
}
return ln, nil
return ln, err
}
func fdSetup(fd int, sa syscall.Sockaddr, addr string) error {
var err error
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
return fmt.Errorf("cannot enable SO_REUSEADDR: %s", err)
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReusePort, 1); err != nil {
return &ErrNoReusePort{err}
}
if err = enableDeferAccept(fd); err != nil {
return err
}
if err = enableFastOpen(fd); err != nil {
return err
}
if err = syscall.Bind(fd, sa); err != nil {
return fmt.Errorf("cannot bind to %q: %s", addr, err)
}
if err = syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
return fmt.Errorf("cannot listen on %q: %s", addr, err)
}
return nil
}
func getSockaddr(network, addr string) (sa syscall.Sockaddr, soType int, err error) {
// TODO: add support for tcp networks.
if network != "tcp4" && network != "tcp6" {
return nil, -1, errors.New("only tcp4 and tcp6 network is supported")
}
tcpAddr, err := net.ResolveTCPAddr(network, addr)
if err != nil {
return nil, -1, err
}
switch network {
case "tcp4":
var sa4 syscall.SockaddrInet4
sa4.Port = tcpAddr.Port
copy(sa4.Addr[:], tcpAddr.IP.To4())
return &sa4, syscall.AF_INET, nil
case "tcp6":
var sa6 syscall.SockaddrInet6
sa6.Port = tcpAddr.Port
copy(sa6.Addr[:], tcpAddr.IP.To16())
if tcpAddr.Zone != "" {
ifi, err := net.InterfaceByName(tcpAddr.Zone)
if err != nil {
return nil, -1, err
}
sa6.ZoneId = uint32(ifi.Index)
}
return &sa6, syscall.AF_INET6, nil
default:
return nil, -1, errors.New("Unknown network type " + network)
}
var cfg = &tcplisten.Config{
ReusePort: true,
DeferAccept: true,
FastOpen: true,
}
-19
View File
@@ -1,19 +0,0 @@
// +build darwin dragonfly freebsd netbsd openbsd rumprun
package reuseport
import (
"syscall"
)
const soReusePort = syscall.SO_REUSEPORT
func enableDeferAccept(fd int) error {
// TODO: implement SO_ACCEPTFILTER:dataready here
return nil
}
func enableFastOpen(fd int) error {
// TODO: implement TCP_FASTOPEN when it will be ready
return nil
}
-29
View File
@@ -1,29 +0,0 @@
// +build linux
package reuseport
import (
"fmt"
"syscall"
)
const (
soReusePort = 0x0F
tcpFastOpen = 0x17
)
func enableDeferAccept(fd int) error {
if err := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_DEFER_ACCEPT, 1); err != nil {
return fmt.Errorf("cannot enable TCP_DEFER_ACCEPT: %s", err)
}
return nil
}
func enableFastOpen(fd int) error {
if err := syscall.SetsockoptInt(fd, syscall.SOL_TCP, tcpFastOpen, fastOpenQlen); err != nil {
return fmt.Errorf("cannot enable TCP_FASTOPEN(qlen=%d): %s", fastOpenQlen, err)
}
return nil
}
const fastOpenQlen = 16 * 1024