Go - 实现超时的最佳方式

Go - The most optimal way to implement a timeout

我有一个异步部署的服务,我需要等待指定的时间才能上线。如果指定的时间过去了,我们仍然无法找到该服务,那么我们就会出错。在 go 中编写这个的最佳方式是什么?我正在研究使用 context.WithTimeout 但不确定它究竟如何工作。感谢您的帮助!

func (c *Client) WaitForServiceToComeAlive(ctx context.Context, name string, timeout time.Duration) error {
    
    var mysvc *Service
    var err error

    endtime := time.Now().Add(timeout)

    for time.Now().Before(endtime) {
        mysvc, err = c.GetService(ctx, name)
        if err != nil {
            return err
        }

        if mysvc != nil {
            break
        }

        time.Sleep(time.Second * 10)
    }

    if mysvc == nil {
        return fmt.Errorf("svc %s did not register", name)
    }

    return nil
}

永远不要使用 time.Sleep,尤其是在很长一段时间内 - 因为它是不间断的。为什么这很重要?如果它在一个 goroutine 中并且该任务不会完成(即上下文被取消),那么您宁愿立即中止。

所以要创建一个带取消的轮询等待:

select {
case <-ctx.Done():               // cancel early if context is canceled
    return ctx.Err()
case <-time.After(pollInterval): // wait for pollInterval duration
}

在输入上下文中设置更大的超时:

ctx := context.TODO() // <- outer request context goes here or context.Background()

// wrap context with a timeout
ctx, cancel := context.WithTimeout(ctx, 1 * time.Minute)
defer cancel() // avoid leaks

err := c.WaitForServiceToComeAlive(ctx, "job", 10*time.Second /* poll interval */)

那么您的服务等待函数将简化为:

func (c *Client) WaitForServiceToComeAlive(ctx context.Context, name string, pollInterval time.Duration) error {

    var mysvc *Service
    var err error

    for {
        mysvc, err = c.GetService(name) // <- this should take a ctx too - if possible for early cancelation
        if err != nil {
            return err
        }
        if mysvc != nil {
            return nil
        }

        select {
        case <-ctx.Done():
            return ctx.Err()
        case <-time.After(pollInterval):
        }
    }

}

https://play.golang.org/p/JwH5CMyY0I2