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 在获取boss直聘的聊天的时候只能获取到前40条聊天数据
  • ¥20 关于URL获取的参数,无法执行二选一查询
  • ¥15 液位控制,当液位超过高限时常开触点59闭合,直到液位低于低限时,断开
  • ¥15 marlin编译错误,如何解决?
  • ¥15 有偿四位数,节约算法和扫描算法
  • ¥15 VUE项目怎么运行,系统打不开
  • ¥50 pointpillars等目标检测算法怎么融合注意力机制
  • ¥20 Vs code Mac系统 PHP Debug调试环境配置
  • ¥60 大一项目课,微信小程序
  • ¥15 求视频摘要youtube和ovp数据集