Предотвращение превышения крайнего срока контекста (клиент.Превышен тайм-аут при ожидании заголовков) ошибка с HTTP 200 OK

#go #tcp #server-timing

#Вперед #tcp #сервер-синхронизация

Вопрос:

У меня есть клиент и сервер. Сервер обрабатывает запрос более 2 секунд. Но у клиента не так много времени. Ответ должен быть получен через 1 секунду. Вот почему он отвечает

Получить «http://localhost:8888 «: превышен предельный срок контекста (Клиент.Превышен тайм-аут при ожидании заголовков) паника: ошибка времени выполнения: неверный адрес памяти или разыменование нулевого указателя

Вопрос? Как я могу изменить server.go таким образом, чтобы все ошибки были исправлены должным образом и чтобы конечная точка всегда была доступна? Обратите внимание, что конечная точка, вызываемая клиентом, должна обработать запрос как можно быстрее и вернуть 200 OK, как только это будет сделано.

client.go

 package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

func main() {

    c := amp;http.Client{Timeout: 2 * time.Second}

    res, err := c.Get("http://localhost:8888")
    if err != nil {
        log.Println(err)
    }
    var r []byte

    _, err = res.Body.Read(r)
    fmt.Println(string(r))
}
  

server.go

 package main

import (
    "fmt"
    "io"
    "net/http"
    "time"
)

func slowHandler(w http.ResponseWriter, req *http.Request) {
    time.Sleep(2 * time.Second)
    io.WriteString(w, "I am slow!n")
}

func main() {
    srv := http.Server{
        Addr:    ":8888",
        Handler: http.HandlerFunc(slowHandler),
    }

    if err := srv.ListenAndServe(); err != nil {
        fmt.Printf("Server failed: %sn", err)
    }
}
  

Ответ №1:

вы можете оправиться от паники, внедрив промежуточное программное обеспечение для восстановления, что-то вроде:

  defer func() {
      if err := recover(); err != nil {
        log.Println("recovered from panic", err)
        fmt.Fprintf(w, "recovered from panic")
      }
    }()
  

вы можете использовать эту полезную статью в качестве руководства https://www.nicolasmerouze.com/middlewares-golang-best-practices-examples

Редактировать

вы можете создать пользовательское промежуточное программное обеспечение для обработки тайм-аута пользовательского обработчика

 func timeOutMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        done := make(chan bool)
        ctx, cancelFunc := context.WithTimeout(r.Context(), time.Second*1)
        defer cancelFunc()
        go func() {
            next.ServeHTTP(w, r)
            close(done)
        }()
        select {
        case <-done:
            return
        case <-ctx.Done():
            w.WriteHeader(http.StatusOK)
            w.Write([]byte(`{"message": "handled time out"}`))
        }
    })

}

  

для того, чтобы это сработало, вам необходимо выполнить синхронизацию с клиентом, потому что, если клиент установит меньший тайм-аут, он никогда не получит надлежащего ответа, или, в случае, если сервер установит очень низкий тайм-аут, это также может произойти.

также для полного чтения байтов ответа используйте это

 defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
  

Комментарии:

1. ах, мой плохой, не сервер паникует, это клиент, потому что вы читаете нулевой ответ из-за тайм-аута, в вашем примере клиент никогда не получает ответа от сервера, таким образом, r равно нулю и панике. на данный момент вы можете сделать две вещи: — обработка ошибок клиента if err, ok := err.(net.Error); ok amp;amp; err.Timeout() { — тайм-аут обработчика сервера Handler: http.TimeoutHandler(http.HandlerFunc(slowHandler), 1*time.Second, "I timed out!n"), этот вернет 503, но вернется, как только истечет время ожидания

2. Предположим, что эта служба является общедоступной, и она требует непредсказуемого времени, и клиентов может быть много. Некоторые могут назначать другим на 1 секунду больше. В этом случае ваш код не работает,

3. Я не уверен, что то, что вы говорите, вообще возможно, единственное, о чем я могу подумать, это получить параметр / заголовок в запросе, а затем настроить время ожидания контекста в соответствии с этим, иначе вы не можете знать, какое время ожидания клиента установлено.

4. К сожалению, вы не можете получить тайм-аут клиента из заголовка запроса.

5. Я имел в виду пользовательский заголовок / параметр, который клиент должен знать заранее