HTTP-цикл HandleFunc на тайм-аут? - PullRequest
       33

HTTP-цикл HandleFunc на тайм-аут?

0 голосов
/ 06 ноября 2019

Я работаю над приложением Go, которое имеет веб-сервер. Я пытался добавить таймауты и столкнулся с проблемой. Вот пример кода, который я сделал, чтобы воспроизвести его, потому что публикация реального кода была бы невозможна:

package main

import (
    "fmt"
    "html/template"
    "net/http"
    "time"
)

var layout *template.Template

func main() {
    router := http.NewServeMux()
    server := &http.Server{
        Addr:         ":8888",
        Handler:      router,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 1 * time.Second,
        IdleTimeout:  15 * time.Second,
    }

    router.HandleFunc("/", home)

    var err error
    layout, err = template.ParseFiles("./layout.html")
    if err != nil {
        fmt.Printf("Error1: %+v\n", err)
    }

    server.ListenAndServe()
}

func home(w http.ResponseWriter, r *http.Request) {
    fmt.Println("responding")
    err := layout.Execute(w, template.HTML(`World`))
    if err != nil {
        fmt.Printf("Error2: %+v\n", err)
    }
    time.Sleep(5 * time.Second)
}

layout.html: Hello {{.}}!

Когда я запускаю егои посетите 127.0.0.1:8888, браузер продолжает загружаться, и home(), который запускает тайм-аут, запускается снова, и он делает это 10 раз, прежде чем останавливается, и браузер показывает ошибку сброса соединения.

Я ожидал, что по истечении времени ожидания функция будет немедленно завершена, соединение будет закрыто, а браузер прекратит загрузку и выдаст ошибку.

Как мне этого добиться?

1 Ответ

0 голосов
/ 07 ноября 2019

немедленный ответ с использованием goroutines и времени ожидания контекста

package main

import (
    "context"
    "fmt"
    "html/template"
    "net/http"
    "time"
)

var layout *template.Template
var WriteTimeout = 1 * time.Second

func main() {
    router := http.NewServeMux()
    server := &http.Server{
        Addr:         ":8889",
        Handler:      router,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: WriteTimeout + 10*time.Millisecond, //10ms Redundant time
        IdleTimeout:  15 * time.Second,
    }
    router.HandleFunc("/", home)
    server.ListenAndServe()
}

func home(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("responding\n")
    ctx, _ := context.WithTimeout(context.Background(), WriteTimeout)
    worker, cancel := context.WithCancel(context.Background())
    var buffer string
    go func() {
        // do something
        time.Sleep(2 * time.Second)
        buffer = "ready all response\n"
        //do another
        time.Sleep(2 * time.Second)
        cancel()
        fmt.Printf("worker finish\n")
    }()
    select {
    case <-ctx.Done():
        //add more friendly tips
        w.WriteHeader(http.StatusInternalServerError)
        return
    case <-worker.Done():
        w.Write([]byte(buffer))
        fmt.Printf("writed\n")
        return
    }
}
...