(tested) Add reuseport feauture.

SO_REUSEPORT allows multipple instances to use same port.
It might be used for better scalability and performance.

* update dependencies
* fix data race of http's srv and tlsSrv variables
This commit is contained in:
Lawrence, Rendall
2022-07-11 13:32:18 +03:00
parent 25de42e3d0
commit 9d8440667c
5 changed files with 101 additions and 28 deletions
+5
View File
@@ -28,6 +28,8 @@ mochi:
# The path to the required files to listen via HTTPS.
tls_cert_path: ""
tls_key_path: ""
# Enable SO_REUSEPORT to allow starting multiple mochi instances with the same HTTP(S) port.
reuse_port: true
# The timeout durations for HTTP requests.
read_timeout: 5s
@@ -95,6 +97,9 @@ mochi:
# BitTorrent traffic.
addr: "0.0.0.0:6969"
# Enable SO_REUSEPORT to allow starting multiple mochi instances with the same UDP port.
reuse_port: true
# The leeway for a timestamp on a connection ID.
max_clock_skew: 10s
+56 -16
View File
@@ -6,11 +6,14 @@ import (
"context"
"crypto/tls"
"errors"
"net"
"net/http"
"net/netip"
"sync"
"time"
"github.com/julienschmidt/httprouter"
"github.com/libp2p/go-reuseport"
"github.com/sot-tech/mochi/bittorrent"
"github.com/sot-tech/mochi/frontend"
@@ -33,6 +36,7 @@ type Config struct {
EnableKeepAlive bool `cfg:"enable_keepalive"`
TLSCertPath string `cfg:"tls_cert_path"`
TLSKeyPath string `cfg:"tls_key_path"`
ReusePort bool `cfg:"reuse_port"`
AnnounceRoutes []string `cfg:"announce_routes"`
ScrapeRoutes []string `cfg:"scrape_routes"`
PingRoutes []string `cfg:"ping_routes"`
@@ -92,9 +96,11 @@ func (cfg Config) Validate() Config {
// Frontend represents the state of an HTTP BitTorrent Frontend.
type Frontend struct {
srv *http.Server
tlsSrv *http.Server
tlsCfg *tls.Config
srv *http.Server
srvMu sync.Mutex
tlsSrv *http.Server
tlsSrvMu sync.Mutex
tlsCfg *tls.Config
logic frontend.TrackerLogic
Config
@@ -110,8 +116,10 @@ func NewFrontend(logic frontend.TrackerLogic, c conf.MapConfig) (*Frontend, erro
cfg := provided.Validate()
f := &Frontend{
logic: logic,
Config: cfg,
logic: logic,
Config: cfg,
srvMu: sync.Mutex{},
tlsSrvMu: sync.Mutex{},
}
if cfg.Addr == "" && cfg.HTTPSAddr == "" {
@@ -177,12 +185,17 @@ func NewFrontend(logic frontend.TrackerLogic, c conf.MapConfig) (*Frontend, erro
func (f *Frontend) Stop() stop.Result {
stopGroup := stop.NewGroup()
f.srvMu.Lock()
if f.srv != nil {
stopGroup.AddFunc(f.makeStopFunc(f.srv))
}
f.srvMu.Unlock()
f.tlsSrvMu.Lock()
if f.tlsSrv != nil {
stopGroup.AddFunc(f.makeStopFunc(f.tlsSrv))
}
f.tlsSrvMu.Unlock()
return stopGroup.Stop()
}
@@ -211,20 +224,47 @@ func (f *Frontend) serveHTTP(handler http.Handler, tls bool) error {
var err error
if tls {
srv.Addr = f.HTTPSAddr
srv.TLSConfig = f.tlsCfg
f.tlsSrv = srv
err = srv.ListenAndServeTLS("", "")
if f.ReusePort {
var ln net.Listener
if ln, err = reuseport.Listen("tcp", f.HTTPSAddr); err == nil {
defer ln.Close()
srv.TLSConfig = f.tlsCfg
f.tlsSrvMu.Lock()
f.tlsSrv = srv
f.tlsSrvMu.Unlock()
err = srv.ServeTLS(ln, "", "")
}
} else {
srv.Addr = f.HTTPSAddr
srv.TLSConfig = f.tlsCfg
f.tlsSrvMu.Lock()
f.tlsSrv = srv
f.tlsSrvMu.Unlock()
err = srv.ListenAndServeTLS("", "")
}
} else {
srv.Addr = f.Addr
f.srv = srv
err = srv.ListenAndServe()
if f.ReusePort {
var ln net.Listener
if ln, err = reuseport.Listen("tcp", f.Addr); err == nil {
defer ln.Close()
f.srvMu.Lock()
f.srv = srv
f.srvMu.Unlock()
err = srv.Serve(ln)
}
} else {
srv.Addr = f.Addr
f.srvMu.Lock()
f.srv = srv
f.srvMu.Unlock()
err = srv.ListenAndServe()
}
}
// Start the HTTP server.
if !errors.Is(err, http.ErrServerClosed) {
return err
if errors.Is(err, http.ErrServerClosed) {
err = nil
}
return nil
return err
}
func injectRouteParamsToContext(ctx context.Context, ps httprouter.Params) context.Context {
@@ -308,7 +348,7 @@ func (f *Frontend) scrapeRoute(w http.ResponseWriter, r *http.Request, ps httpro
go f.logic.AfterScrape(ctx, req, resp)
}
func (f Frontend) ping(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
func (f *Frontend) ping(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var err error
if r.Method == http.MethodGet {
err = f.logic.Ping(context.TODO())
+23 -8
View File
@@ -13,6 +13,8 @@ import (
"sync"
"time"
"github.com/libp2p/go-reuseport"
"github.com/sot-tech/mochi/bittorrent"
"github.com/sot-tech/mochi/frontend"
"github.com/sot-tech/mochi/frontend/udp/bytepool"
@@ -23,14 +25,17 @@ import (
"github.com/sot-tech/mochi/pkg/timecache"
)
var logger = log.NewLogger("udp frontend")
var allowedGeneratedPrivateKeyRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
var (
logger = log.NewLogger("udp frontend")
allowedGeneratedPrivateKeyRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
errUnexpectedConnType = errors.New("unexpected connection type (not UDPConn)")
)
// Config represents all of the configurable options for a UDP BitTorrent
// Tracker.
type Config struct {
Addr string
ReusePort bool `cfg:"reuse_port"`
PrivateKey string `cfg:"private_key"`
MaxClockSkew time.Duration `cfg:"max_clock_skew"`
EnableRequestTiming bool `cfg:"enable_request_timing"`
@@ -130,12 +135,22 @@ func (t *Frontend) Stop() stop.Result {
}
// listen resolves the address and binds the server socket.
func (t *Frontend) listen() error {
udpAddr, err := net.ResolveUDPAddr("udp", t.Addr)
if err != nil {
return err
func (t *Frontend) listen() (err error) {
if t.ReusePort {
var ln net.PacketConn
if ln, err = reuseport.ListenPacket("udp", t.Addr); err == nil {
var isOk bool
if t.socket, isOk = ln.(*net.UDPConn); !isOk {
err = errUnexpectedConnType
}
}
} else {
var udpAddr *net.UDPAddr
udpAddr, err = net.ResolveUDPAddr("udp", t.Addr)
if err == nil {
t.socket, err = net.ListenUDP("udp", udpAddr)
}
}
t.socket, err = net.ListenUDP("udp", udpAddr)
return err
}
+5 -4
View File
@@ -5,7 +5,7 @@ go 1.18
require (
code.cloudfoundry.org/go-diodes v0.0.0-20220601181242-ac2da19efd60
github.com/SermoDigital/jose v0.9.2-0.20180104203859-803625baeddc
github.com/anacrolix/torrent v1.45.0
github.com/anacrolix/torrent v1.46.0
github.com/go-redis/redis/v8 v8.11.5
github.com/jackc/pgx/v4 v4.16.1
github.com/julienschmidt/httprouter v1.3.0
@@ -16,12 +16,13 @@ require (
github.com/prometheus/client_golang v1.12.2
github.com/rs/zerolog v1.27.0
github.com/stretchr/testify v1.8.0
github.com/libp2p/go-reuseport v0.2.0 // indirect
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/anacrolix/dht/v2 v2.17.0 // indirect
github.com/anacrolix/log v0.13.1 // indirect
github.com/anacrolix/dht/v2 v2.18.0 // indirect
github.com/anacrolix/log v0.13.2-0.20220426014722-7b7d13a55d55 // indirect
github.com/anacrolix/missinggo v1.3.0 // indirect
github.com/anacrolix/missinggo/v2 v2.7.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -49,7 +50,7 @@ require (
github.com/prometheus/common v0.35.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 // indirect
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/protobuf v1.28.0 // indirect
)
+12
View File
@@ -52,6 +52,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/anacrolix/dht/v2 v2.17.0 h1:MrAS6XqVCqNyyskTwxZB2sqhU/GGUdecb2TNe2b2QjE=
github.com/anacrolix/dht/v2 v2.17.0/go.mod h1:osiyaNrMLG9dw7wUtVMaII/NdCjlXeHjUcYzXnmop68=
github.com/anacrolix/dht/v2 v2.18.0 h1:btjVjzjKqO5nKGbJHJ2UmuwiRx+EgX3e+OCHC9+WRz8=
github.com/anacrolix/dht/v2 v2.18.0/go.mod h1:mxrSeP/LIY429SgWMO9o6UdjBjB8ZjBh6HHCmd8Ly1g=
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
@@ -60,6 +62,8 @@ github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgw
github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
github.com/anacrolix/log v0.13.1 h1:BmVwTdxHd5VcNrLylgKwph4P4wf+5VvPgOK4yi91fTY=
github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68=
github.com/anacrolix/log v0.13.2-0.20220426014722-7b7d13a55d55 h1:22o7jDD18WtIYUa2I983X58o6t8e9GVbcdYOKoy+Y4g=
github.com/anacrolix/log v0.13.2-0.20220426014722-7b7d13a55d55/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68=
github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y=
@@ -76,6 +80,8 @@ github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pm
github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8=
github.com/anacrolix/torrent v1.45.0 h1:dmRfw+kSl+UtBk2gAOpJgdnu3OzWsSmE6sMTunifpdw=
github.com/anacrolix/torrent v1.45.0/go.mod h1:511SlHgyD0LAeN5CIJw8CSfS4BaBVDtfVkRSMHPjfAQ=
github.com/anacrolix/torrent v1.46.0 h1:m5SGlW4p0dJkqrIh4bCqzcKbKFFmfJorf9LSpSM5dEc=
github.com/anacrolix/torrent v1.46.0/go.mod h1:3DE+VA4AgyfKDPjZcIo70D3VFZRo3bfdEBn70CGjca4=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -217,6 +223,7 @@ github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@@ -237,6 +244,7 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
@@ -299,6 +307,8 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560=
github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
@@ -583,6 +593,8 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 h1:wEZYwx+kK+KlZ0hpvP2Ls1Xr4+RWnlzGFwPP0aiDjIU=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d h1:/m5NbqQelATgoSPVC2Z23sR4kVNokFwDDyWh/3rGY+I=
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=