在 Go 中创建空闲超时?

Creating an idle timeout in Go?

我将 CloudFlare 用于我的一个高流量网站,它位于我的堆栈前面。

问题是,CloudFlare 除了创建新连接外,还会打开空闲连接,这不是我可以更改的设置。

当我让 Varnish 或 Nginx 坐在前面侦听端口 80 时,它们具有开箱即用的配置来挂断空闲连接。

这很好,直到我不得不将一个用 Go 编写的代理添加到我的堆栈的前面。它使用 net/http 标准库。

我不是 Go 高手,但根据人们告诉我的情况,只有读写超时设置,但不会挂断空闲连接。

现在我的服务器会被连接填满然后挂掉,除非我设置了一组读写超时,但问题是我的后端有时会花费很长时间,这会导致好的请求在它们应该被切断的时候被切断't。

使用 Go http 处理空闲连接的正确方法是什么?

编辑 1: 更清楚地说,我正在使用 httputil.NewSingleHostReverseProxy 构建一个代理,它公开传输选项但仅针对上游。我遇到的问题是下游问题,需要在使用 ReverseProxy 作为处理程序的 http.Server 对象上设置它们。 http.Server 不公开传输。

编辑 2:我更喜欢空闲超时而不是读取超时,因为后者适用于活跃的上传者。

谢谢

请参阅 net/http.Transport 文档。 Transport 类型有一些选项用于处理处于保持活动状态的空闲 HTTP 连接。通过阅读您的问题,似乎与您的问题最相关的选项是 MaxIdleConnsPerHost 字段:

MaxIdleConnsPerHost, if non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used.

阅读代码,默认为每个主机 2 个。

传输类型也有一个方法来关闭所有空闲连接:CloseIdleConnections。

CloseIdleConnections closes any connections which were previously connected from previous requests but are now sitting idle in a "keep-alive" state. It does not interrupt any connections currently in use.

您可以在任何 http 客户端上指定传输:

tr := &http.Transport{
    TLSClientConfig:    &tls.Config{RootCAs: pool},
    DisableCompression: true,
    MaxIdleConnsPerHost: 1,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")

另一件值得注意的事情:文档建议您保留一个在所有请求中重复使用的 http 客户端对象(即像全局变量一样)。

Clients and Transports are safe for concurrent use by multiple goroutines and for efficiency should only be created once and re-used.

如果您在代理实现中创建许多 http 客户端对象,它可能会解释空闲连接的无限增长(尽管只是猜测您可能如何实现它)。

编辑:多读一点,net/httputil 包有一些方便的反向代理类型。参见ReverseProxy type。该结构还允许您提供自己的传输对象,从而允许您通过此帮助程序类型控制代理的空闲客户端行为。

在 Go http 服务器中挂断空闲连接的正确方法是设置 read timeout

没有必要设置写入超时以挂断空闲客户端。如果它会切断响应,请不要设置或调整它。

如果您的上传时间较长,请使用 a connection state callback 实现单独的空闲和读取超时:

server.ConnState = func(c net.Conn, cs http.ConnState) {
    switch cs {
    case http.StateIdle, http.StateNew:
        c.SetReadDeadline(time.Now() + idleTimeout)
    case http.StateActive:
        c.SetReadDeadline(time.Now() + activeTimeout)
    }
}