英文:
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
}
这段代码的作用是,在指定的时间内等待服务上线。它使用了一个循环来检查服务是否已经上线,每隔10秒钟检查一次。如果超过指定的时间仍然没有找到服务,就会返回一个错误。如果找到了服务,就会跳出循环并返回nil。
你提到了使用context.WithTimeout
,这是一个用于设置超时的函数。你可以在调用GetService
的地方使用context.WithTimeout
来设置超时时间,如果超时了就会返回一个错误。这样可以避免使用循环和time.Sleep
来等待服务上线。
以下是使用context.WithTimeout
的示例代码:
func (c *Client) WaitForServiceToComeAlive(ctx context.Context, name string, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
mysvc, err := c.GetService(ctx, name)
if err != nil {
return err
}
if mysvc == nil {
return fmt.Errorf("svc %s did not register", name)
}
return nil
}
这样,如果超过指定的时间仍然没有找到服务,GetService
函数会返回一个超时错误。
英文:
I have a service that is deployed asynchronously and I need to wait a specified amount of time for it to come online. If the specified amount of time elapses and we still aren't able to find the service, then we error out. What is the most optimal way of writing this in go? I was looking into using context.WithTimeout
but not sure exactly how this would work. Thanks for the help!
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
}
答案1
得分: 2
永远不要使用time.Sleep
,尤其是在长时间间隔时,因为它是不可中断的。为什么这很重要?如果它在一个goroutine中,并且该任务不会完成(即上下文被取消),那么你宁愿立即中止。
因此,要创建一个带有取消功能的轮询等待:
select {
case <-ctx.Done(): // 如果上下文被取消,则提前取消
return ctx.Err()
case <-time.After(pollInterval): // 等待pollInterval的时间
}
将较长的超时时间放在输入上下文中:
ctx := context.TODO() // <- 外部请求上下文放在这里或者使用context.Background()
// 使用超时时间包装上下文
ctx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel() // 避免泄漏
err := c.WaitForServiceToComeAlive(ctx, "job", 10*time.Second /* 轮询间隔 */)
然后你的服务等待函数简化为:
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) // <- 如果可能的话,这里也应该接收一个上下文,以便提前取消
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
英文:
Never use time.Sleep
especially if it's a long period - as it's uninterruptible. Why does this matter? If its in a goroutine and that task is not going to complete (i.e. context was canceled), then you would rather abort immediately.
So to create a polling-wait with cancelation:
select {
case <-ctx.Done(): // cancel early if context is canceled
return ctx.Err()
case <-time.After(pollInterval): // wait for pollInterval duration
}
Put your larger timeout within the input context:
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 */)
then your service-wait function simplifies to:
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):
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论