package fiberimport ()const ( envPreforkChildKey = "FIBER_PREFORK_CHILD" envPreforkChildVal = "1")var ( testPreforkMaster = false testOnPrefork = false)// IsChild determines if the current process is a child of Preforkfunc () bool {returnos.Getenv(envPreforkChildKey) == envPreforkChildVal}// prefork manages child processes to make use of the OS REUSEPORT or REUSEADDR featurefunc ( *App) (, string, *tls.Config) error {// 👶 child process 👶ifIsChild() {// use 1 cpu core per child processruntime.GOMAXPROCS(1)// Linux will use SO_REUSEPORT and Windows falls back to SO_REUSEADDR // Only tcp4 or tcp6 is supported when preforking, both are not supported , := reuseport.Listen(, )if != nil {if !.config.DisableStartupMessage {const = 100 * time.Millisecondtime.Sleep() // avoid colliding with startup message }returnfmt.Errorf("prefork: %w", ) }// wrap a tls config around the listener if providedif != nil { = tls.NewListener(, ) }// kill current child proc when master exitsgowatchMaster()// prepare the server for the start .startupProcess()// listen for incoming connectionsreturn .server.Serve() }// 👮 master process 👮typestruct {interror }// create variables := runtime.GOMAXPROCS(0) := make(map[int]*exec.Cmd) := make(chan , )// kill child procs when master exitsdeferfunc() {for , := range {if := .Process.Kill(); != nil {if !errors.Is(, os.ErrProcessDone) {log.Errorf("prefork: failed to kill child: %v", ) } } } }()// collect child pidsvar []string// launch child procsfor := 0; < ; ++ { := exec.Command(os.Args[0], os.Args[1:]...) //nolint:gosec // It's fine to launch the same process againiftestPreforkMaster {// When test prefork master, // just start the child process with a dummy cmd, // which will exit soon = dummyCmd() } .Stdout = os.Stdout .Stderr = os.Stderr// add fiber prefork child flag into child proc env .Env = append(os.Environ(),fmt.Sprintf("%s=%s", envPreforkChildKey, envPreforkChildVal), )if := .Start(); != nil {returnfmt.Errorf("failed to start a child prefork process, error: %w", ) }// store child process := .Process.Pid [] = = append(, strconv.Itoa())// execute fork hookif .hooks != nil {iftestOnPrefork { .hooks.executeOnForkHooks(dummyPid) } else { .hooks.executeOnForkHooks() } }// notify master if child crashesgofunc() { <- {, .Wait()} }() }// Run onListen hooks // Hooks have to be run here as different as non-prefork mode due to they should run as child or master .runOnListenHooks(.prepareListenData(, != nil))// Print startup messageif !.config.DisableStartupMessage { .startupMessage(, != nil, ","+strings.Join(, ",")) }// return error if child crashesreturn (<-).}// watchMaster watches child procsfunc watchMaster() {ifruntime.GOOS == "windows" {// finds parent process, // and waits for it to exit , := os.FindProcess(os.Getppid())if == nil { _, _ = .Wait() //nolint:errcheck // It is fine to ignore the error here }os.Exit(1) //nolint:revive // Calling os.Exit is fine here in the prefork }// if it is equal to 1 (init process ID), // it indicates that the master process has exitedconst = 500 * time.Millisecondforrangetime.NewTicker().C {ifos.Getppid() == 1 {os.Exit(1) //nolint:revive // Calling os.Exit is fine here in the prefork } }}var ( dummyPid = 1 dummyChildCmd atomic.Value)// dummyCmd is for internal prefork testingfunc dummyCmd() *exec.Cmd { := "go"if := dummyChildCmd.Load(); != nil && != "" { = .(string) //nolint:forcetypeassert,errcheck // We always store a string in here }ifruntime.GOOS == "windows" {returnexec.Command("cmd", "/C", , "version") }returnexec.Command(, "version")}
The pages are generated with Goldsv0.6.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.