Browse Source

SO_REUSEPORT support

DarthSim 5 years ago
parent
commit
c76696caf7
7 changed files with 62 additions and 9 deletions
  1. 3 0
      config.go
  2. 1 0
      docs/configuration.md
  3. 1 1
      go.mod
  4. 15 0
      listen_no_reuseport.go
  5. 33 0
      listen_reuseport.go
  6. 8 6
      prometheus.go
  7. 1 2
      server.go

+ 3 - 0
config.go

@@ -136,6 +136,7 @@ type config struct {
 	Concurrency      int
 	MaxClients       int
 	TTL              int
+	SoReuseport      bool
 
 	MaxSrcDimension    int
 	MaxSrcResolution   int
@@ -258,6 +259,8 @@ func configure() {
 
 	intEnvConfig(&conf.TTL, "IMGPROXY_TTL")
 
+	boolEnvConfig(&conf.SoReuseport, "IMGPROXY_SO_REUSEPORT")
+
 	intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
 	megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
 	intEnvConfig(&conf.MaxSrcFileSize, "IMGPROXY_MAX_SRC_FILE_SIZE")

+ 1 - 0
docs/configuration.md

@@ -34,6 +34,7 @@ $ echo $(xxd -g 2 -l 64 -p /dev/random | tr -d '\n')
 * `IMGPROXY_CONCURRENCY`: the maximum number of image requests to be processed simultaneously. Default: number of CPU cores times two;
 * `IMGPROXY_MAX_CLIENTS`: the maximum number of simultaneous active connections. Default: `IMGPROXY_CONCURRENCY * 10`;
 * `IMGPROXY_TTL`: duration (in seconds) sent in `Expires` and `Cache-Control: max-age` HTTP headers. Default: `3600` (1 hour);
+* `IMGPROXY_SO_REUSEPORT`: when `true`, enables `SO_REUSEPORT` socket option (currently on linux and darwin only);
 * `IMGPROXY_USER_AGENT`: User-Agent header that will be sent with source image request. Default: `imgproxy/%current_version`;
 * `IMGPROXY_USE_ETAG`: when `true`, enables using [ETag](https://en.wikipedia.org/wiki/HTTP_ETag) HTTP header for HTTP cache control. Default: false;
 

+ 1 - 1
go.mod

@@ -35,7 +35,7 @@ require (
 	golang.org/x/net v0.0.0-20190110200230-915654e7eabc
 	golang.org/x/oauth2 v0.0.0-20190110195249-fd3eaa146cbb // indirect
 	golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
-	golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb // indirect
+	golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb
 	google.golang.org/api v0.1.0
 	google.golang.org/genproto v0.0.0-20190110221437-6909d8a4a91b // indirect
 )

+ 15 - 0
listen_no_reuseport.go

@@ -0,0 +1,15 @@
+// +build !linux,!darwin !go1.11
+
+package main
+
+import (
+	"net"
+)
+
+func listenReuseport(network, address string) (net.Listener, error) {
+	if conf.SoReuseport {
+		logWarning("SO_REUSEPORT support is not implemented for your OS or Go version")
+	}
+
+	return net.Listen(network, address)
+}

+ 33 - 0
listen_reuseport.go

@@ -0,0 +1,33 @@
+// +build linux darwin
+// +build go1.11
+
+package main
+
+import (
+	"context"
+	"net"
+	"syscall"
+
+	"golang.org/x/sys/unix"
+)
+
+func listenReuseport(network, address string) (net.Listener, error) {
+	if !conf.SoReuseport {
+		return net.Listen(network, address)
+	}
+
+	lc := net.ListenConfig{
+		Control: func(_, _ string, c syscall.RawConn) error {
+			var cerr error
+			err := c.Control(func(fd uintptr) {
+				cerr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
+			})
+			if err != nil {
+				return err
+			}
+			return cerr
+		},
+	}
+
+	return lc.Listen(context.Background(), network, address)
+}

+ 8 - 6
prometheus.go

@@ -100,14 +100,16 @@ func initPrometheus() {
 
 	prometheusEnabled = true
 
-	s := http.Server{
-		Addr:    conf.PrometheusBind,
-		Handler: promhttp.Handler(),
-	}
+	s := http.Server{Handler: promhttp.Handler()}
 
 	go func() {
-		logNotice("Starting Prometheus server at %s\n", s.Addr)
-		if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+		l, err := listenReuseport("tcp", conf.PrometheusBind)
+		if err != nil {
+			logFatal(err.Error())
+		}
+
+		logNotice("Starting Prometheus server at %s\n", conf.PrometheusBind)
+		if err := s.Serve(l); err != nil && err != http.ErrServerClosed {
 			logFatal(err.Error())
 		}
 	}()

+ 1 - 2
server.go

@@ -4,7 +4,6 @@ import (
 	"context"
 	"crypto/subtle"
 	"fmt"
-	"net"
 	"net/http"
 	"time"
 
@@ -30,7 +29,7 @@ func buildRouter() *router {
 }
 
 func startServer() *http.Server {
-	l, err := net.Listen("tcp", conf.Bind)
+	l, err := listenReuseport("tcp", conf.Bind)
 	if err != nil {
 		logFatal(err.Error())
 	}