-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
prefork.go
101 lines (91 loc) · 2.44 KB
/
prefork.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package fiber
import (
"crypto/tls"
"flag"
"fmt"
"net"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"time"
utils "github.com/gofiber/utils"
reuseport "github.com/valyala/fasthttp/reuseport"
)
var (
flagPrefork = "-prefork"
flagChild = "-prefork-child"
isPrefork bool
isChild bool
)
func init() { //nolint:gochecknoinits
// Avoid panic when the user adds their own flags and runs `flag.Parse()`
flag.BoolVar(&isPrefork, flagChild[1:], false, "use prefork")
flag.BoolVar(&isChild, flagPrefork[1:], false, "is child proc")
}
// prefork manages child processes to make use of the OS REUSEPORT or REUSEADDR feature
func (app *App) prefork(addr string, tlsconfig ...*tls.Config) (err error) {
// 👶 child process 👶
if utils.GetArgument(flagChild) {
// use 1 cpu core per child process
runtime.GOMAXPROCS(1)
var ln net.Listener
// get an SO_REUSEPORT listener or SO_REUSEADDR for windows
if ln, err = reuseport.Listen("tcp4", addr); err != nil {
if !app.Settings.DisableStartupMessage {
time.Sleep(100 * time.Millisecond) // avoid colliding with startup message
}
return fmt.Errorf("prefork %v", err)
}
// wrap a tls config around the listener if provided
if len(tlsconfig) > 0 {
ln = tls.NewListener(ln, tlsconfig[0])
}
// listen for incoming connections
return app.server.Serve(ln)
}
// 👮 master process 👮
type child struct {
pid int
err error
}
// create variables
var max = runtime.GOMAXPROCS(0)
var childs = make(map[int]*exec.Cmd)
var channel = make(chan child, max)
// kill child procs when master exits
defer func() {
for _, proc := range childs {
_ = proc.Process.Kill()
}
}()
// collect child pids
pids := []string{}
// launch child procs
for i := 0; i < max; i++ {
/* #nosec G204 */
cmd := exec.Command(os.Args[0], append(os.Args[1:], flagChild)...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Start(); err != nil {
return fmt.Errorf("failed to start a child prefork process, error: %v\n", err)
}
// store child process
childs[cmd.Process.Pid] = cmd
pids = append(pids, strconv.Itoa(cmd.Process.Pid))
// notify master if child crashes
go func() {
channel <- child{cmd.Process.Pid, cmd.Wait()}
}()
}
// Print startup message
if !app.Settings.DisableStartupMessage {
app.startupMessage(addr, len(tlsconfig) > 0, ","+strings.Join(pids, ","))
}
// return error if child crashes
for sig := range channel {
return sig.err
}
return
}