云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

香港带宽_乘鸾百度云_精选特惠

小七 141 0

Go net/http超时完整指南

在Go中编写HTTP服务器或客户机时,超时是最容易出错和最微妙的事情之一:有很多可供选择的,一个错误在很长一段时间内不会产生任何后果,直到网络出现故障,进程挂起。HTTP是一个复杂的多阶段协议,因此没有一个万能的解决方案来解决超时问题。考虑一下流式端点、JSON API和Comet端点。事实上,违约往往不是你想要的。在这篇文章中,我将分解您可能需要应用超时的各个阶段,并研究在服务器端和客户端实现超时的不同方法。设置截止日期首先,您需要了解Go为实现超时而公开的网络原语:截止期。暴露于网络连接在设定的[读写]期限内(时间。时间)方法中,截止期是一个绝对时间,当到达该时间时,所有I/O操作都将失败并出现超时错误。最后期限不是超时。一旦设置,它们将永远有效(或直到下次调用SetDeadline),而不管在此期间是否以及如何使用连接。因此,要使用SetDeadline构建超时,必须在每次读/写操作之前调用它。您可能不想自己调用SetDeadline,而是让net/http为您调用它,使用它的高级超时。但是,请记住,所有超时都是根据截止日期来实现的,因此它们不会在每次发送或接收数据时重置。服务器超时"soyouwantoexposetgooninternetwork"的帖子有更多关于服务器超时的信息,特别是关于HTTP/2和go1.7错误的信息。对于暴露在Internet上的HTTP服务器来说,在客户端连接上强制执行超时是至关重要的。否则,非常慢或消失的客户端可能会泄漏文件描述符,并最终导致以下情况:http:Accept错误:Accept tcp[::]:80:accept4:打开的文件太多;5毫秒后重试中暴露了两个超时http服务器:ReadTimeout和WriteTimeout。通过显式使用服务器来设置它们:srv:=&http服务器{读取超时:5*时间。秒,写入时间:10*时间。秒,}日志。打印(李斯特和服务())ReadTimeout涵盖了从接受连接到完全读取请求主体的时间(如果您确实读取了主体,否则到头的末尾)。它是在net/http中通过在Accept之后立即调用SetReadDeadline来实现的。WriteTimeout通常覆盖从请求头读取结束到响应写入结束的时间(也就是ServeHTTP的生存期),方法是在readRequest结束时调用SetWriteDeadline。但是,当连接是HTTPS时,SetWriteDeadline将在Accept之后立即调用,以便它也覆盖作为TLS握手的一部分而写入的数据包。令人恼火的是,这意味着(仅在这种情况下)WriteTimeout最终包括头read和第一个字节wait。在处理不受信任的客户端和/或网络时,应该设置两个超时,这样客户端就不会因为读写速度慢而中断连接。最后,还有http.TimeoutHandler. 它不是一个服务器参数,而是一个限制ServeHTTP调用最长持续时间的处理程序包装器。它的工作原理是缓冲响应,如果超过截止时间,则发送504网关超时。请注意,它在1.6中是断开的,在1.6.2中是固定的。http.listendserve做错事了吗顺便说一句,这意味着包级别的便利功能绕过了http服务器像http.listendserve, http.listendservetls以及http.服务不适合公共互联网服务器。这些函数将超时保留为默认的关闭值,无法启用它们,因此如果使用它们,很快就会泄漏连接并耗尽文件描述符。我至少犯过六次这个错误。相反,创建一个http服务器实例使用ReadTimeout和WriteTimeout,并使用其相应的方法,如上面几段中的示例所示。关于流媒体非常令人恼火的是,根本无法访问底层网络连接因此,打算流化响应的服务器将被迫取消WriteTimeout(这也可能是它们默认为0的原因)。这是因为没有网络连接访问时,无法在每次写入之前调用SetWriteDeadline来实现适当的空闲(非绝对)超时。而且,没有办法取消被阻止的回复作者。写因为回复作者。关闭(您可以通过接口升级访问)没有记录来取消阻止并发写入。所以也没有办法用计时器手动建立超时。遗憾的是,这意味着流媒体服务器无法真正保护自己免受慢速客户端的攻击。我提交了一个问题和一些建议,我欢迎那里的反馈。客户端超时客户端超时可以更简单,也可以更复杂,这取决于您使用的是哪种超时,但对于防止资源泄漏或陷入困境同样重要。最容易使用的是http.客户端. 它涵盖了整个交换过程,从拨号(如果连接不被重用)到读取主体。c:=&http.客户端{超时:15*时间。秒,}响应,错误:=c.Get("https://blog.filippo.io/")与上面的服务器端案例一样,包级别的功能包括http.Get使用没有超时的客户端,因此在开放的互联网上使用是很危险的。对于更精细的控制,您可以设置许多其他更具体的超时:net.Dialer.Timeout限制建立TCP连接所花费的时间(如果需要新的连接)。http.Transport.TLSHandshakeTimeout限制执行TLS握手所花费的时间。http.Transport.ResponseHeaderTimeout限制读取响应标头所花的时间。http.Transport.ExpectContinueTimeout限制客户端在包含Expect:100 continue和接收go-ahead以发送正文之间等待的时间。请注意,在1.6中设置此选项将禁用HTTP/2(DefaultTransport是1.6.2中的特殊情况)。c:=&http.客户端{运输:&http.传输{拨号:(&网络拨号器{超时:30*时间。秒,保持有效:30*时间。秒,}).拨号,TLSHandshakeTimeout:10*时间。秒,响应负责人超时:10*时间。秒,预期持续时间:1*时间。秒,}}据我所知,没有办法限制发送请求的具体时间。读取请求正文所花费的时间可以用时间。计时器因为它发生在客户机方法返回之后(请参见下面有关如何取消请求的信息)。最后,1.7中的新功能http.Transport.idlecontimeout. 它不控制客户端请求的阻塞阶段,而是控制空闲连接在连接池中保留的时间。请注意,默认情况下,客户端将遵循重定向。http.Client.Timeout包括跟踪重定向所花费的所有时间,而细粒度超时是针对每个请求的,因为http.传输是一个没有重定向概念的低级系统。取消和上下文net/http提供了两种取消客户端请求的方法:请求。取消而且,1.7中新增了上下文。请求。取消是一个可选通道,当设置并关闭时,会导致请求中止,就像请求。超时被击中了。(它们实际上是通过相同的机制实现的,在写这篇文章的时候,我发现了1.7中的一个错误,所有的取消都将作为超时错误返回。)我们可以利用请求。取消以及时间。计时器为了建立一个允许流式传输的更精细的超时,每次成功从主体读取数据时都会将截止日期往后推:主包装进口("io""io/ioutil""日志""网络/http""时间")函数main(){c:=make(chan struct{})定时器:=AfterFunc时间(五)*时间。秒,函数(){关闭(c)})//每秒提供256个字节。请求,错误:=http.NewRequest("获取"http://httpbin.org/range/2048?持续时间=8,区块大小=256",无)如果出错!等于零{日志。致命(错误)}请求取消=c日志。打印("正在发送请求…")响应,错误:=http.DefaultClient.Do(要求)如果出错!等于零{日志。致命(错误)}推迟resp.Body.关闭()日志。打印("阅读体…")为{定时器。复位(2*时间。秒)//试试看:定时器。复位(50*时间。毫秒)_,错误=io.CopyN公司(ioutil.丢弃, 责任主体,256)如果错误==io.EOF{打破}否则如果出错!等于零{日志。致命(错误)}}}在上面的例子中,我们在请求的Do阶段设置了5秒的超时,但是之后我们在8轮中至少花费8秒来读取主体,每次超时为2秒。我们可以一直这样流下去,不会有被卡住的风险。如果我们不能接收超过2秒的身体数据,那么io.CopyN公司将返回net/http:request cancelled。在1.7中,上下文包升级到标准库。关于上下文有很多东西需要学习,但是对于我们的目的,您应该知道它们取代和反对请求。取消.要使用上下文取消请求,我们只需使用上下文.WithCancel并使用请求.WithContext. 当我们想取消请求时,我们通过调用cancel()来取消上下文(而不是关闭cancel通道):ctx,取消:=上下文.WithCancel(上下文.TODO())定时器:=AfterFunc时间(五)*时间。秒,函数(){取消()})请求,错误:=http.NewRequest("获取"http://httpbin.org/range/2048?持续时间=8,区块大小=256",无)如果出错!等于零{日志。致命(错误)}需求=请求与上下文(ctx)上下文的优点是如果父上下文上下文.WithCancel)如果被取消,我们的命令也将在整个管道中传播。就这些。我希望我没有超过你的阅读截止日期!如果你觉得这种深入了解Go标准图书馆的做法听起来很有趣,那就知道我们正在伦敦、奥斯汀(德克萨斯州)、香槟(伊利诺伊州)、旧金山和新加坡招聘。