Home  >  Article  >  Backend Development  >  Goroutines called recursively by time.AfterFunc() are poorly designed

Goroutines called recursively by time.AfterFunc() are poorly designed

WBOY
WBOYforward
2024-02-09 13:48:21820browse

由 time.AfterFunc() 递归调用的 goroutine 的设计很糟糕

php editor Xigua believes that the sentence "The design of goroutine called recursively by time.AfterFunc() is very bad" reflects an unreasonable design idea. In concurrent programming, recursively calling goroutines may cause excessive resource consumption, and even cause problems such as deadlock and memory overflow. Therefore, recursive calls should be used with caution and other alternatives to solve the problem should be considered to ensure the performance and stability of the program. When writing code, we should always pay attention to the rationality of the design to avoid unnecessary problems.

Question content

I have a small http application (A). On startup, it calls another http service (B) to verify the license and if the license is OK, the http server (A) starts. If verification fails A, a fatal error occurs and exit

License checks are performed every 24 hours

Would it be considered a bad design to recursively create a new goroutine every 24 hours? Check my code below. Will the previous goroutine be closed or continue to run, and then n goroutines will call each other and end

Is each new goroutine called from the main goroutine or from the child goroutine?

License verification module. A Inspection Service B

func Request(retry bool) error {
    // request and verify license (external http service)
    err := verify_license()
    if err != nil {
        return err
    }
    
    if retry {
        //  Renew verification timeout (renew license every 24 hours)
        time.AfterFunc(LICENSE_TIMEOUT, func(){
            request_retry()
        })
    }
    
    return nil
}

func request_retry(){
    for i := 0; i < LICENSE_RETRY; i++ {
        if err := v.Request(false); err == nil {
            break
        }
        
        time.Sleep(LICENSE_RETRY_TIMEOUT)
    }
    
    time.Sleep(LICENSE_TIMEOUT)
    v.Request(true)
}

In the main package before HTTP server (A) starts

if err := license_verify.Request(true); err != nil {
    log.Fatal(err.Error())
}

Solution

Maybe you can rethink the design of the problem. For example:

func main() {
    if !checkLicense() {
        log.Fatal("license check failed")
    }

    srv := http.Server{} // 
    
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    go func() {
         for {
             select {
             case <-ctx.Done(): // in case you want to instal signal handlers etc
                  return
             case <-time.After(24 * time.Hour):
                  if !checkLicense() {
                      cancel() // or srv.Shtdown, really depends on you
                  }
             }
         }
    }()

    if err := srv.ListenAndServe(); err != nil {
        log.Fatal(err)
    }
}

func checkLicense() bool {
    // add retries per request here
}

Basically, what it does is create a goroutine that checks periodically and notifies the context or channel if something goes wrong.

If I understand the question correctly, you just need to keep it simple. One building block is to retry a request if it fails. Otherwise, there is a 24-hour retry. The final layer is to react to the check if it fails. You can use context, channel, or whatever you really like

The above is the detailed content of Goroutines called recursively by time.AfterFunc() are poorly designed. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete