英文:
Creating an idle timeout in Go?
问题
我在我的一个高流量网站上使用CloudFlare,并且它位于我的堆栈前面。
问题是,CloudFlare除了创建新连接外,还会保持空闲连接打开,而且这不是我可以更改的设置。
当我在前面使用Varnish或Nginx监听端口80时,它们都有开箱即用的配置来挂断空闲连接。
这一切都很好,直到我不得不在我的堆栈前面添加一个用Go编写的代理。它使用net/http
标准库。
我不是Go的专家,但根据人们告诉我的情况,它只有读取和写入超时设置,而没有挂断空闲连接的设置。
现在,我的服务器将充满连接并且会崩溃,除非我设置读取和写入超时,但这样做的问题是,我的后端有时需要很长时间,这会导致本不应该被中断的良好请求被中断。
在Go http中处理空闲连接的正确方法是什么?
**编辑1:**为了更清楚,我正在使用httputil.NewSingleHostReverseProxy
构建一个代理,它公开了传输选项,但只针对上游。我遇到的问题是下游的问题,它们需要在使用ReverseProxy作为处理程序的http.Server
对象上设置。http.Server
不公开传输。
**编辑2:**与其使用读取超时,我更喜欢使用空闲超时,因为后者将适用于活动上传者。
谢谢
英文:
I use CloudFlare for one of my high volume websites, and it sits in front of my stack.
The thing is CloudFlare leaves idle connections open in addition to creating new ones, and it's not a setting I can change.
When I have Varnish or Nginx sitting in front listening on port 80 they have out of the box configuration to hang up the idle connections.
This is fine until I had to add a proxy written in Go to the front of my stack. It uses the net/http
standard library.
I'm not a Go wizard but based on what people are telling me there are only read and write timeout settings but not hanging up idle connections.
Now my server will fill up with connections and die unless I set a set read and write timeouts, but the problem with this is my backend takes a long time sometimes and it's causing good requests to be cut off when they shouldn't.
What is the proper way to handle idle connections with Go http?
Edit 1: To be more clear, I'm using httputil.NewSingleHostReverseProxy
to construct a proxy, which exposes transport options but only for the upstream. The problems I am having are downstream, they need to be set on the http.Server
object that uses the ReverseProxy as a handler. http.Server
does not expose transport.
Edit 2: I would prefer an idle timeout to a read timeout, since the later would apply to an active uploader.
Thanks
答案1
得分: 6
在Go http服务器中,挂断空闲连接的正确方法是设置读取超时。
不需要设置写入超时来挂断空闲客户端。不要设置此值,或者如果它截断响应,请将其调整为更长的时间。
如果您有长时间的上传操作,可以使用连接状态回调来实现单独的空闲和读取超时:
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)
}
}
英文:
The proper way to hangup idle connections in the Go http server is to set the read timeout.
It is not necessary to set the write timeout to hang up on idle clients. Don't set this value or adjust it up if it's cutting off responses.
If you have long uploads, then use a connection state callback to implement separate idle and read timeouts:
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)
}
}
答案2
得分: 0
请参阅net/http.Transport文档。Transport类型具有一些选项,用于处理处于keep-alive状态的空闲HTTP连接。从阅读您的问题来看,与您的问题最相关的选项似乎是MaxIdleConnsPerHost字段:
MaxIdleConnsPerHost,如果非零,则控制每个主机保持的最大空闲(keep-alive)连接数。如果为零,则使用DefaultMaxIdleConnsPerHost。
从代码中可以看出,默认值为每个主机2个空闲连接。
Transport类型还有一个方法可以关闭所有空闲连接:CloseIdleConnections。
CloseIdleConnections关闭之前从先前的请求连接到的但现在处于“keep-alive”状态的任何连接。它不会中断当前正在使用的任何连接。
您可以在任何http客户端上指定一个Transport:
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和Transports可以安全地被多个goroutine并发使用,并且为了效率,应该只创建一次并重复使用。
如果您在代理实现中创建了许多http客户端对象,这可能解释了空闲连接的无限增长(只是猜测您可能是如何实现的)。
编辑:再读一点,net/httputil包中有一些方便的类型用于反向代理。请参阅ReverseProxy类型。该结构还允许您提供自己的Transport对象,通过这个辅助类型来控制代理的空闲客户端行为。
英文:
See the net/http.Transport docs. The Transport type has some options for dealing with idle HTTP connections in the keep-alive state. From reading your question, the option that seems most relevant to your problem is the MaxIdleConnsPerHost field:
> MaxIdleConnsPerHost, if non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used.
Reading the code, the default is 2 per host.
The Transport type also has a method to zap all idle connections: 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.
You can specify a Transport on any http client:
tr := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool},
DisableCompression: true,
MaxIdleConnsPerHost: 1,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")
Another thing worth noting: the docs recommend that you keep a single http client object that is re-used across all your requests (i.e. like a global variable).
> Clients and Transports are safe for concurrent use by multiple goroutines and for efficiency should only be created once and re-used.
If you are creating many http client objects in your proxy implementation, it might explain unbounded growth of idle connections (just guessing at how you might be implementing this, though).
EDIT: Reading a little bit more, the net/httputil package has some convenience types for reverse proxies. See the ReverseProxy type. That struct also allows you to supply your own Transport object, allowing you to control your proxy's idle client behavior via this helper type.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论