我正在创建类似于Twitter firehose / streaming API的流API。

据我所知,这是基于保持打开状态的HTTP连接的,当后端获取数据时,它会写入被阻止的HTTP连接。 看来,我编写的任何代码都会在任何东西连接后立即关闭HTTP连接。

有没有办法保持这种开放?

1
2
3
4
5
6
7
8
9
10
func startHTTP(pathPrefix string) {
    log.Println("Starting HTTPS Server")
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        // Wait here until a write happens to w
        // Or we timeout, we can reset this timeout after each write
    })

    log.Print("HTTPS listening on :5556")
    log.Fatal(http.ListenAndServeTLS(":5556", pathPrefix+".crt", pathPrefix+".key", nil))
}
  • 如果您不想关闭连接,请不要从处理程序中返回。 处理程序是"处理"连接的内容。
  • 是的,所以它到达了函数的结尾并返回并因此关闭了连接。"阻塞"功能并能够从另一个goroutine写入w的最佳方法是什么?
  • 您可以尝试将其放入具有选择语句的无限for循环中,这样您就可以捕获发生的任何错误以关闭任何错误的连接
  • 阻止任何功能的方式相同。 您需要与其他goroutine协调,将其传递给ResponseWriter,然后等待其完成。 如果您为此运行了goroutine,则必须已经有某种模式来调度和等待它们。
  • 好的,这很有意义,每个新的HTTP连接也会启动goroutine吗?
  • 是的,每个连接处理程序都在自己的goroutine中; goroutines是Go处理并发的方式。

当您不希望在某个事件之后立即向客户端发送HTTP响应时,称为长轮询。

这是一个长轮询的简单示例,该轮询在客户端断开连接时取消请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

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

func longOperation(ctx context.Context, ch chan<- string) {
    // Simulate long operation.
    // Change it to more than 10 seconds to get server timeout.
    select {
    case <-time.After(time.Second * 3):
        ch <-"Successful result."
    case <-ctx.Done():
        close(ch)
    }
}

func handler(w http.ResponseWriter, _ *http.Request) {
    notifier, ok := w.(http.CloseNotifier)
    if !ok {
        panic("Expected http.ResponseWriter to be an http.CloseNotifier")
    }

    ctx, cancel := context.WithCancel(context.Background())
    ch := make(chan string)
    go longOperation(ctx, ch)

    select {
    case result := <-ch:
        fmt.Fprint(w, result)
        cancel()
        return
    case <-time.After(time.Second * 10):
        fmt.Fprint(w,"Server is busy.")
    case <-notifier.CloseNotify():
        fmt.Println("Client has disconnected.")
    }
    cancel()
    <-ch
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe("localhost:8080", nil)
}

网址:

  • Golang:匿名结构和空结构。
  • 从Go服务器发送分块的HTTP响应。
  • 并发模式:上下文。
  • 要点:

  • Golang长轮询示例。
  • 请求取消的Golang长轮询示例。
    • 谢谢。 它与我最终得到的结果相似。 我要解决的问题是,如果客户端在长时间操作期间断开连接,会发生什么情况
    • 检查CloseNotifier golang.org/pkg/net/http/#CloseNotifier和context包blog.golang.org/context
    • 香港专业教育学院更新了我的答案,使它更完整。
    • WebSockets是长轮询的现代替代方案...连接保持打开状态,因此可以从打开的Websocket上的客户端或服务器端发送流量
    • @berserkk很好,我没有考虑使用上下文,但是很好地整理了它!