Как проверить ошибку тайм-аута http в Go?

Новое в пути. В настоящее время это мой код:

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, я мог напрямую увидеть тип исключения и проверить его, как мне это сделать в будущем?

Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
0
0
111
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В настоящее время вы бы не сделали этого таким образом, и установка тайм-аута на клиенте остается в соответствии с гарантией стабильности API .

Сегодня вы настроили клиент и выполнили запрос с контекстом .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
}

Run on playground

´if urlErr, isUrlErr := err.(*url.Error); isUrlErr´ у меня работает. Я не вижу преимуществ в новом API (хотя кажется, что хуже), но, возможно, это дальше по моему пути. Спасибо за подробный ответ.

Peheje 10.03.2024 13:52

@Peheje Ну, вы можете передать один контекст нескольким экземплярам чего угодно, и тем самым вы можете гарантировать, что вся операция, включающая несколько действий, не займет больше времени, чем ваш тайм-аут. Теперь представьте, что вы хотите изящно остановить множество операций и подумайте о том, как context.WithCancel(ctx) может вам здесь помочь... И когда вы все равно используете context, вы также можете использовать context.WithTimeout.

Markus W Mahlberg 10.03.2024 14:37

Другие вопросы по теме