如何在 Go 中检查 http 超时错误?

问题描述 投票:0回答:1

新去。这是目前我的代码:

client := http.Client{
    Timeout: 10 * time.Millisecond,
}
resp, err := client.Get("http://google.com/")
if err != nil {
    if os.IsTimeout(err) {
        fmt.Println("There was a timeout")
    }
    panic(err)
}

这可行,但是 os.IsTimeout 写道:

// This function predates errors.Is, and the notion of whether an
// error indicates a timeout can be ambiguous. For example, the Unix
// error EWOULDBLOCK sometimes indicates a timeout and sometimes does not.
// New code should use errors.Is with a value appropriate to the call
// returning the error, such as os.ErrDeadlineExceeded.
if errors.Is(err, os.ErrDeadlineExceeded) {
    fmt.Println("There was a timeout 2")
}

这不起作用。我尝试了调试,但没有一种简单的方法可以让我了解如何检查特定的错误类型。来自.NET,我可以直接看到异常类型,并检查它,将来我该如何处理?

go http error-handling
1个回答
0
投票

从今天开始,您不会这样做,并且根据 api 稳定性保证,在客户端上设置超时。

今天,您将设置一个客户端并使用

context.WithTimeout
发出请求。

TL;DR:如果请求在收到响应之前超时,则上下文将为“完成”;如果在超时之前返回响应,则上下文不会为“完成”。

package main

import (
    "context"
    "io"
    "log"
    "net/http"
    "net/url"
    "time"
)

func main() {

    // Start a http server to test the timeout
    srv := setupServer()
    defer srv.Shutdown(context.Background())

    // Examle 1: Using a context with a timeout for a request
    // =======================================================

    // create a default http client
    client := &http.Client{}
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)

    // Release resources when done
    defer cancel()

    // Create a new request with our context
    req, _ := http.NewRequestWithContext(ctx, "GET", "http://localhost:8080/hellotimeout", nil)

    // Start a timer
    start := time.Now()
    // Make the request
    _, err := client.Do(req)

    // Check if the request was anything other than a timeout
    // Note that we could check for the timeout error here.
    // However, we will use a select statement to demonstrate how to handle the timeout.
    if urlErr, isURLErr := err.(*url.Error); isURLErr && !urlErr.Timeout() {
        log.Printf("Something went wrong: %s", urlErr)
        return
    }

    select {
    // If the request times out, the context will be done.
    // If the request is completed here, the context will not be done and the default case will be executed.
    case <-ctx.Done():
        log.Printf("Request timed out after %s", time.Since(start))
    default:
        log.Printf("Request completed after %s", time.Since(start))
        log.Println("Processing response...")
    }

    // Examle 2: The same, but this time the request does not timeout
    // ==============================================================

    // Create a new request with our context
    ctx2, cancel2 := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel2()
    req, _ = http.NewRequestWithContext(ctx2, "GET", "http://localhost:8080/hello", nil)

    // Start a timer
    start = time.Now()
    // Make the request
    resp, err := client.Do(req)

    // Check if the request was anything other than a timeout
    if urlErr, isURLErr := err.(*url.Error); isURLErr && !urlErr.Timeout() {
        log.Printf("Something went wrong: %s", urlErr)
        return
    }

    select {
    case <-ctx2.Done():
        log.Printf("Request timed out after %s", time.Since(start))
        return
    default:
        log.Printf("Request completed after %s", time.Since(start))
    }
    log.Println("Processing response...")
    io.Copy(log.Writer(), resp.Body)
}

func setupServer() *http.Server {
    // Setup a server so we can test the timeout
    srv := &http.Server{Addr: ":8080"}

    http.Handle("/hellotimeout", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Simulate a long running request
        time.Sleep(5 * time.Second)
        w.Write([]byte("Hello, World!"))
    }))

    http.Handle("/hello", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Respond immediately
        w.Write([]byte("Hello, World!"))
    }))

    // Start the server in "background"

    go srv.ListenAndServe()
    return srv
}

在操场上跑步

© www.soinside.com 2019 - 2024. All rights reserved.