问题描述
设置
我有两个不同的应用程序,都是用 Go 编写的。第一个是服务器,第二个是调用服务器的较小的应用程序。他们使用 http
包进行调用,使用 router
包设置端点。
问题
当设备对服务器进行特定调用时,会返回 408 (StatusRequestTimeout) 响应。此响应不是由于我们的服务器实际超时,而只是用于描述错误(更多信息见下文)。设备第一次进行此调用时,它会收到 408 并正常进行。但是,如果再次进行相同的调用,则会在第二个调用完成后立即将新的“第三个”调用发送到服务器。第三次调用与前两次调用相同。没有为此调用启用 http 重试逻辑。
为什么要发出第三个电话?当代码更新为返回 400 状态响应而不是 408 时,不再进行第三次调用。此外,更改不同的调用以返回 408 而不是 400 将开始表现出与发送三次三次调用相同的行为。我一直找不到解释这种行为的文档,也找不到其他描述这种行为的文章。
预感
我发现很多像 this 这样的文章表明浏览器有时会重试请求。此外,其他一些类似 this 的 stackoverflow 帖子表明 http 请求不会在没有设置我们自己的重试逻辑的情况下重试。同样,我们已经设置了它,但是没有为这个给定的调用启用它,并且调试显示我们从来没有进入我们的自定义重试逻辑。
我相信这是一个铬功能。我试图用 Firefox 复制这个,但我没有成功,但是 Edge 表现出相同的行为。然而,Chrome 开发工具(和边缘)只显示两个网络调用,第一个和第三个。我认为它也可能是http库,但很奇怪的是浏览器之间的行为不同。
错误修复
鉴于 408 响应应该包含的性质,我决定不再将它们用于自定义错误响应。在这一点上,我只是更好奇为什么会出现这种行为,我的预感是否正确,或者是否有其他因素在起作用。
解决方法
让我们从方法 is408Message()
开始,它是 here。它检查缓冲区是否带有 408 Request timeout
状态代码。另一个 method 使用此方法检查来自服务器的响应,在 408 Request Timeout
的情况下,persistConn
会因 errServerClosedIdle
错误而关闭。错误是 assigned 到 persistConn.closed
字段。
在 http Transport
的 main loop 中,调用 persistConn.roundTrip
here 作为错误返回存储在 persistConn.closed
字段中的值。几行below,您可以找到一个名为pconn.shouldRetryRequest
的方法,该方法将persistConn.roundTrip
返回的错误作为参数,并在错误为errServerClosedIdle
时返回true。由于整个操作都被 for 循环包裹,因此请求将再次发送。
分析 shouldRetryRequest
method 可能对您很有价值,因为必须满足多个条件才能重试请求。例如第一次使用连接时不会重复请求。