繁体   English   中英

在 Go 中使用 context.WithTimeout() 时的最佳实践是什么?

[英]What is the best practice when using with context.WithTimeout() in Go?

我想使用context.WithTimeout()来处理我发出外部请求的用例,如果请求的响应过长,它会返回错误。

我已经实现了类似于下面附加的操场链接的伪代码: 2 解决方案:

  • 主要 -> 不是预期的
  • main_1 -> 预期
package main

import (
    "context"
    "fmt"
    "time"
)

// I just dummy sleep in this func to produce use case this func
// need 10s to process and handle logic. 
// And this assume will be out of timeOut expect (5s)
func makeHTTPRequest(ctx context.Context) (string, error) {
    time.Sleep(time.Duration(10) * time.Second)
    return "abc", nil
}

// In main Func, I will set timeout is 5 second. 
func main() {
    var strCh = make(chan string, 1)
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(5)*time.Second)
    defer cancel()

    fmt.Print("Begin make request\n")
    abc, err := makeHTTPRequest(ctx)
    if err != nil {
        fmt.Print("Return error\n")
        return
    }

    select {
    case <-ctx.Done():
        fmt.Printf("Return ctx error: %s\n", ctx.Err())
        return
    case strCh <- abc:
        fmt.Print("Return response\n")
        return
    }
}

func main_1() {
    var strCh = make(chan string, 1)
    var errCh = make(chan error, 1)
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(5)*time.Second)
    defer cancel()

    go func() {
        fmt.Print("Begin make request\n")
        abc, err := makeHTTPRequest(ctx)
        if err != nil {
            fmt.Print("Return error\n")
            errCh <- err
            return
        }
        strCh <- abc

    }()
    select {
    case err := <-errCh:
        fmt.Printf("Return error: %s\n", err.Error())
        return
    case <-ctx.Done():
        fmt.Printf("Return ctx error: %s\n", ctx.Err())
        return
    case str := <-strCh:
        fmt.Printf("Return response: %s\n", str)
        return
    }
}

但是,如果使用main() function 则它不会按预期工作。 但是,如果使用 goroutine 使用第二个main_1()实现,那么新的context.WithTimeout()可能会按预期工作。

你能帮我回答这个问题吗?

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

最好在makeHTTPRequest() function 中处理上下文,因此您可以在main()中将其用作同步 function 。

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

func makeHTTPRequest(ctx context.Context) (string, error) {
    ch := make(chan string)

    go func() {
        time.Sleep(10 * time.Second)
        select {
        case ch <- "abc":
        default:
            // When context deadline exceeded, there is no receiver
            // This case will prevent goroutine blocking forever
            return
        }
    }()

    select {
    case <-ctx.Done():
        return "", ctx.Err()
    case result := <-ch:
        return result, nil
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    fmt.Printf("[%v] Begin make request \n", time.Now())
    abc, err := makeHTTPRequest(ctx)
    if err != nil {
        fmt.Printf("[%v] Return error: %v \n", time.Now(), err)
        return
    }
    fmt.Printf("[%v] %s", time.Now(), abc)
}

如果我没听错的话。 有两个问题。

  1. 您想知道为什么 main() function 不起作用?
  2. 最佳做法是什么?

第一季度

main()在 makeHTTPRequest 处被阻塞,并且在此期间,上下文超时。 所以,不能按预期工作。

第二季度

这个例子可以回答你。 main_1()中,您的代码已经是最佳实践。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2023 STACKOOM.COM