1

我想知道为 Echo 库上的特定路由设置超时的最佳方法。

我用context.WithTimeout实现了超时,但是使用了几个函数来封装上下文,我认为这是错误的。

ctx, cancel := context.WithTimeout(ctx, 30*time.Second)

是否有任何中间件或更好的方法来实现这一目标?

4

1 回答 1

3

向服务器 context.Context 添加超时

在不确切知道您要做什么的情况下回答这个问题有点棘手。我将首先回答如何使用WithTimeout中间件处理上下文包装。

中间件可以像这样添加/修改请求上下文:

func TimeoutMiddleware(timeout time.Duration, next func(w http.ResponseWriter, req *http.Request)) func(w http.ResponseWriter, req *http.Request) {
    return func(w http.ResponseWriter, req *http.Request) {
        // Wrap the existing context from the request
        ctx, cancel := context.WithTimeout(req.Context(), timeout)
        // Always do this to clean up contexts, otherwise they'll hang out
        // and gather since they are blocked go rountines
        defer cancel()

        // Put the new context into the request
        req = req.WithContext(ctx)

        // Pass the modified request forward
        next(w, req)

        // Handle any ctx timeout issues in the general middleware
        if err := ctx.Err(); err != nil {
            if errors.Is(err, context.DeadlineExceeded) {
                log.Println("HTTP Request timed out")
                w.Write([]byte("Timed out"))
            }
        }
    }
}

问题是next(w, req)需要您的 http 处理程序来处理上下文超时。如果 http 处理程序忽略上下文,它不会超时。比如这样:

func endless(w http.ResponseWriter, req *http.Request) {
    ctx := req.Context()
    // Just a busy loop....
    for {
        select {
        case <-ctx.Done():
            // Exit the handler if the context is Done(), even if our function is not.
            return
        default:
            fmt.Println("wait")
            time.Sleep(1 * time.Second)
        }
    }
}

如果您在处理程序中进行数据库调用或需要时间的操作,通常context.Context情况下,如果上下文被取消,数据库 api 会接受提前中止。

因此,此解决方案将超时添加到进入您仍需要管理的处理程序的请求上下文。


客户端超时

您始终可以将超时添加到您的请求中:

    client := http.Client{
        Timeout: time.Second,
    }
    client.Do(...)
于 2021-02-01T01:06:42.690 回答