doubai9014 2014-11-24 02:37
浏览 108

Http服务器读写超时和服务器端事件

I'm writing a test app with SSE, but my problem is that ReadTimeout and WriteTimeout are closing the clients connection every 10 Seconds and because of this the main page are losing data.

How can I manage this Issue, serving SSE and webpages together without goroutines leak risk and SSE working done?

Server:

server := &http.Server{Addr: addr,
    ReadTimeout:  10 * time.Second,
    WriteTimeout: 10 * time.Second,
    Handler:      s.mainHandler(),
}

Handler:

func sseHandler(w http.ResponseWriter, r *http.Requests) {
    f, ok := w.(http.Flusher)
    if !ok {
        http.Error(w, "Streaming not supported!", http.StatusInternalServerError)
        log.Println("Streaming not supported")
        return
    }
    messageChannel := make(chan string)
    hub.addClient <- messageChannel
    notify := w.(http.CloseNotifier).CloseNotify()

    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")
    for i := 0; i < 1440; {
        select {
        case msg := <-messageChannel:
            jsonData, _ := json.Marshal(msg)
            str := string(jsonData)
            fmt.Fprintf(w, "data: {\"str\": %s, \"time\": \"%v\"}

", str, time.Now())
            f.Flush()

        case <-time.After(time.Second * 60):
            fmt.Fprintf(w, "data: {\"str\": \"No Data\"}

")

            f.Flush()
            i++

        case <-notify:
            f.Flush()
            i = 1440
            hub.removeClient <- messageChannel
        }
    }
}
  • 写回答

1条回答 默认 最新

  • 普通网友 2014-11-25 11:49
    关注

    Both ReadTimeout and WriteTimeout define the duration within which the whole request must be read from or written back to the client. These timeouts are applied to the underlying connection (http://golang.org/src/pkg/net/http/server.go?s=15704:15902) and this is before any headers are received, so you cannot set different limits for separate handlers – all connections within the server will share the same timeout limits.

    Saying this, if you need customised timeouts per request, you will need to implement them in your handler. In your code you're already using timeouts for your job, so this would be a matter of creating a time.After at the beginning of the handler, keep checking (in the handler itself or even pass it around) and stop the request when necessary. This actually gives you more precise control over the timeout, as WriteTimeout would only trigger when trying to write the response (e.g. if the timeout is set to 10 seconds and it takes a minute to prepare the response before any write, you won't get any error until the job is done so you'll waste resources for 50 seconds). From this perspective, I think WriteTimeout itself is good only when your response is ready quite quickly, but you want to drop the connection when network become very slow (or the client deliberately stops receiving data).

    There is a little helper function in the library, http.TimeoutHandler, which behaves similarly to WriteTimeout, but sends back 503 error if the response takes longer than predefined time. You could use it to set up behaviour similar to WriteTimeout per handler, for example:

    package main
    
    import (
        "log"   
        "net/http"
        "time"
    )
    
    type Handler struct {
    }
    
    func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        time.Sleep(3*time.Second)
        // This will return http.ErrHandlerTimeout
        log.Println(w.Write([]byte("body")))
    }
    
    func main() {
        h := &Handler{}
        http.Handle("/t1", http.TimeoutHandler(h, 1*time.Second, ""))
        http.Handle("/t2", http.TimeoutHandler(h, 2*time.Second, ""))   
        http.ListenAndServe(":8080", nil)
    }
    

    This looks very handy, but I found one disadvantage that would affect your code: http.ResponseWriter passed from http.TimeoutHandler doesn't implement http.CloseNotifier. If it's required, you could dive into the implementation and see how they solved the timeout problem in order to come up with a similar, but enhanced solution.

    评论

报告相同问题?

悬赏问题

  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 LiBeAs的带隙等于0.997eV,计算阴离子的N和P
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘
  • ¥15 来真人,不要ai!matlab有关常微分方程的问题求解决,
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误
  • ¥199 rust编程架构设计的方案 有偿
  • ¥15 回答4f系统的像差计算