pkg/server: add retry logic for random port assignment

Change-Id: I70464e344a79dce8eadb9513d2a990faf3b2cca8
This commit is contained in:
Yingrong Zhao 2021-01-28 14:43:47 -05:00 committed by paul cannon
parent fff10b041c
commit 52d6852e58

View File

@ -6,8 +6,12 @@ package server
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"errors"
"net" "net"
"os"
"runtime"
"sync" "sync"
"syscall"
quicgo "github.com/lucas-clemente/quic-go" quicgo "github.com/lucas-clemente/quic-go"
"github.com/zeebo/errs" "github.com/zeebo/errs"
@ -74,16 +78,32 @@ func New(log *zap.Logger, tlsOptions *tlsopts.Options, publicAddr, privateAddr s
Manager: rpc.NewDefaultManagerOptions(), Manager: rpc.NewDefaultManagerOptions(),
} }
publicTCPListener, err := net.Listen("tcp", publicAddr) var err error
var publicTCPListener, publicQUICListener net.Listener
for retry := 0; ; retry++ {
publicTCPListener, err = net.Listen("tcp", publicAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
publicQUICListener, err := quic.NewListener(tlsOptions.ServerTLSConfig(), publicTCPListener.Addr().String(), &quicgo.Config{MaxIdleTimeout: defaultUserTimeout}) publicQUICListener, err = quic.NewListener(tlsOptions.ServerTLSConfig(), publicTCPListener.Addr().String(), &quicgo.Config{MaxIdleTimeout: defaultUserTimeout})
if err != nil { if err != nil {
_, port, _ := net.SplitHostPort(publicAddr)
if port == "0" && retry < 10 && isErrorAddressAlreadyInUse(err) {
// from here, we know for sure that the tcp port chosen by the
// os is available, but we don't know if the same port number
// for udp is also available.
// if a udp port is already in use, we will close the tcp port and retry
// to find one that is available for both udp and tcp.
_ = publicTCPListener.Close()
continue
}
return nil, errs.Combine(err, publicTCPListener.Close()) return nil, errs.Combine(err, publicTCPListener.Close())
} }
break
}
publicMux := drpcmux.New() publicMux := drpcmux.New()
publicTracingHandler := rpctracing.NewHandler(publicMux, jaeger.RemoteTraceHandler) publicTracingHandler := rpctracing.NewHandler(publicMux, jaeger.RemoteTraceHandler)
server.public = public{ server.public = public{
@ -223,3 +243,24 @@ func (p *Server) Run(ctx context.Context) (err error) {
muxCancel() muxCancel()
return errs.Combine(err, muxGroup.Wait()) return errs.Combine(err, muxGroup.Wait())
} }
// isErrorAddressAlreadyInUse checks whether the error is corresponding to
// EADDRINUSE. Taken from https://stackoverflow.com/a/65865898.
func isErrorAddressAlreadyInUse(err error) bool {
var eOsSyscall *os.SyscallError
if !errors.As(err, &eOsSyscall) {
return false
}
var errErrno syscall.Errno
if !errors.As(eOsSyscall.Err, &errErrno) {
return false
}
if errErrno == syscall.EADDRINUSE {
return true
}
const WSAEADDRINUSE = 10048
if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE {
return true
}
return false
}