How best do I keep a long running Go program, running?

后端 未结 6 1987
半阙折子戏
半阙折子戏 2020-12-04 13:22

I\'ve a long running server written in Go. Main fires off several goroutines where the logic of the program executes. After that main does nothing useful. Once main exits

相关标签:
6条回答
  • 2020-12-04 13:33

    Here is a simple block forever using channels

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        done := make(chan bool)
        go forever()
        <-done // Block forever
    }
    
    func forever() {
        for {
            fmt.Printf("%v+\n", time.Now())
            time.Sleep(time.Second)
        }
    }
    
    0 讨论(0)
  • 2020-12-04 13:36

    Block forever. For example,

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func main() {
        go forever()
        select {} // block forever
    }
    
    func forever() {
        for {
            fmt.Printf("%v+\n", time.Now())
            time.Sleep(time.Second)
        }
    }
    
    0 讨论(0)
  • 2020-12-04 13:39

    Nobody mentioned signal.Notify(c chan<- os.Signal, sig ...os.Signal)

    Example:

    package main
    
    import (
        "fmt"
        "time"
        "os"
        "os/signal"
        "syscall"
    )
    
    func main() {
        go forever()
    
        quitChannel := make(chan os.Signal, 1)
        signal.Notify(quitChannel, syscall.SIGINT, syscall.SIGTERM)
        <-quitChannel
        //time for cleanup before exit
        fmt.Println("Adios!")
    }
    
    func forever() {
        for {
            fmt.Printf("%v+\n", time.Now())
            time.Sleep(time.Second)
        }
    }
    
    0 讨论(0)
  • 2020-12-04 13:39

    You could daemonize the process using Supervisor (http://supervisord.org/). Your function forever would just be a process that it runs, and it would handle the part of your function main. You would use the supervisor control interface to start/shutdown/check on your process.

    0 讨论(0)
  • 2020-12-04 13:41

    Go's runtime package has a function called runtime.Goexit that will do exactly what you want.

    Calling Goexit from the main goroutine terminates that goroutine without func main returning. Since func main has not returned, the program continues execution of other goroutines. If all other goroutines exit, the program crashes.

    Example in the playground

    package main
    
    import (
        "fmt"
        "runtime"
        "time"
    )
    
    func main() {
        go func() {
            time.Sleep(time.Second)
            fmt.Println("Go 1")
        }()
        go func() {
            time.Sleep(time.Second * 2)
            fmt.Println("Go 2")
        }()
    
        runtime.Goexit()
    
        fmt.Println("Exit")
    }
    
    0 讨论(0)
  • 2020-12-04 13:43

    The current design of Go's runtime assumes that the programmer is responsible for detecting when to terminate a goroutine and when to terminate the program. The programmer needs to compute the termination condition for goroutines and also for the entire program. A program can be terminated in a normal way by calling os.Exit or by returning from the main() function.

    Creating a channel and delaying exit of main() by immediately receiving on said channel is a valid approach of preventing main from exiting. But it does not solve the problem of detecting when to terminate the program.

    If the number of goroutines cannot be computed before the main() function enters the wait-for-all-goroutines-to-terminate loop, you need to be sending deltas so that main function can keep track of how many goroutines are in flight:

    // Receives the change in the number of goroutines
    var goroutineDelta = make(chan int)
    
    func main() {
        go forever()
    
        numGoroutines := 0
        for diff := range goroutineDelta {
            numGoroutines += diff
            if numGoroutines == 0 { os.Exit(0) }
        }
    }
    
    // Conceptual code
    func forever() {
        for {
            if needToCreateANewGoroutine {
                // Make sure to do this before "go f()", not within f()
                goroutineDelta <- +1
    
                go f()
            }
        }
    }
    
    func f() {
        // When the termination condition for this goroutine is detected, do:
        goroutineDelta <- -1
    }
    

    An alternative approach is to replace the channel with sync.WaitGroup. A drawback of this approach is that wg.Add(int) needs to be called before calling wg.Wait(), so it is necessary to create at least 1 goroutine in main() while subsequent goroutines can be created in any part of the program:

    var wg sync.WaitGroup
    
    func main() {
        // Create at least 1 goroutine
        wg.Add(1)
        go f()
    
        go forever()
        wg.Wait()
    }
    
    // Conceptual code
    func forever() {
        for {
            if needToCreateANewGoroutine {
                wg.Add(1)
                go f()
            }
        }
    }
    
    func f() {
        // When the termination condition for this goroutine is detected, do:
        wg.Done()
    }
    
    0 讨论(0)
提交回复
热议问题