【问题标题】:Detect failed http connections in go在 go 中检测失败的 http 连接
【发布时间】:2019-04-27 19:19:24
【问题描述】:

假设客户端通过http上传大量图像,互联网连接速度很慢,他打开与服务器的连接,开始写入数据,但由于网络问题突然失去了这个连接。如果尚未调用处理程序函数,我如何在服务器上检测到这一点。我找到的解决方案是检查连接状态。但问题是它不可扩展,因为很多 goroutine 会与全局变量交互。还有更优雅的解决方案吗?

package main

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

// current state of connections
var connStates = make(map[string]http.ConnState)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("got new request")
        fmt.Println(connStates)
    })

    srv := &http.Server{
        Addr: ":8080",
        ConnState: func(conn net.Conn, event http.ConnState) {
            if event != http.StateClosed {
                connStates[conn.RemoteAddr().String()] = event
            } else {
                if connStates[conn.RemoteAddr().String()] == http.StateActive {
                    fmt.Println("client cancelled request")
                }
                delete(connStates, conn.RemoteAddr().String())
            }
        },
    }
    log.Fatal(srv.ListenAndServe())
}

【问题讨论】:

  • 您希望对连接状态做什么?也许如果我们对您想用它做什么有更好的想法,我们可能会有一些更好的想法。至于你有什么,我要做的唯一改进是实现sync.Map (golang.org/pkg/sync/#Map) 而不是常规的map
  • 我对连接状态做了什么?如您所见,我有一个全局地图来跟踪所有连接,远程地址作为键(因为它对于每个连接都是唯一的)和连接状态作为值。所以我向服务器添加了ConnState 回调,每次连接状态改变时都会调用它,使用它我支持这个连接映射。最重要的是,检测失败的连接。如您所见,我检查当前状态是否已关闭并且前一个状态是否已打开-http 连接失败,因为没有http.StateActive 状态。
  • 我要问的是你最终想用这些信息做什么?你只是记录它吗?您是否想根据状态执行时间敏感操作,或者您想要做的事情可以延迟一点?我可以看到您目前只是在登录。
  • 是的,我只是想记录这样的事件。

标签: http go


【解决方案1】:

您可以在您的处理程序中使用context,例如,这将检测客户端何时断开连接并返回和http.StatusPartialContent,除了在您可以拥有日志逻辑的地方调用someCleanup()

https://play.golang.org/p/5Yr_HBuyiZW

func helloWorld(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    ch := make(chan struct{})

    go func() {
            time.Sleep(5 * time.Second)
            fmt.Fprintln(w, "Hello World!")
            ch <- struct{}{}
    }()

    select {
    case <-ch:
    case <-ctx.Done():
            http.Error(w, ctx.Err().Error(), http.StatusPartialContent)
            someCleanUP()
    }

}

【讨论】:

  • 你的解决方案的主要问题是,只有当用户完成将数据写入连接并等待响应时才会调用该处理程序,因此当用户断开连接时,你将能够检测到案例服务器处理请求的时间,但不是用户向打开的连接写入数据的时间,实际上这是我需要检测的。不过不管怎样,你的代码 sn-p 真的很有用,谢谢。
  • 我只是不明白为什么你将通道作为参数传递给处理函数,你能澄清一下吗?
  • 嗨@RomanAlexandrovich,这可以更好地解释它stackoverflow.com/a/30183888/1135424,但是,在这种情况下,它可以省略,我更新了答案
【解决方案2】:

如果你只需要日志,你甚至可以简化代码:

srv := &http.Server{
        Addr: ":8080",
        ConnState: func(conn net.Conn, event http.ConnState) {
            log.Printf("addr: %s, changed state to: %s", conn.RemoteAddr(), event.String())
        },
    }

conn 的每次更改都会触发该回调

【讨论】:

    猜你喜欢
    • 2018-12-05
    • 2019-07-13
    • 1970-01-01
    • 1970-01-01
    • 2015-06-19
    • 1970-01-01
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    相关资源
    最近更新 更多