我收到代理服务器返回的零星 502。检查数据包流时,我看到 nginx 向原始服务器已发送 [FIN,ACK] 的套接字发送 POST 请求。我想了解这怎么可能以及任何潜在的解决方案。是源的问题(它仅在发送响应后 5 秒后才发送 FIN、ACK)还是代理的问题?
我的理解:
- 来自源的响应是 [PSH, ACK];
- 代理为使用该 [P.] 接收的数据发送一个 [ACK](wireshark 确认下一个 [ACK] 是针对之前收到的 [PSH-ACK]);
- 7 秒过去了(注意时间戳 btw/ [FIN, ACK] 和我们的 POST ([PSH, ACK]));
- origin 发送一个 [FIN, ACK]。当发送第一个 [FIN, ACK] 时,源 TCP 状态机应该处于 FIN_WAIT_1 状态。
- 然后我们发送另一个 POST 导致 [RST] 作为回报,因为源不期望 [PSH, ACK]。
问题:
- 这种情况的可能解释是什么?
- 如果代理 (nginx) 已经收到 FIN 并且实际上正在确认它,为什么还要发送另一个请求!(POST [PSH, ACK] 数据包中的 ack 号实际上是 [FIN,ACK] 的 SEQ_NUMBER + 1 - 所以它是在确认幻位 FIN。
- 来源仅在 5 秒后而不是立即返回 [FIN,ACK] 的可能原因是什么?读取超时/空闲超时?
我不拥有原产地 - 所以无法在那里捕获。
额外细节:
代理上的错误日志(nginx错误日志):
2017/04/17 06:51:07 [error] 123091#0: *225010841 upstream prematurely closed connection while reading response header from upstream, client: X.90.10, server: www.example.com, request: "POST /web/?a=b HTTP/1.1", upstream: "http://X.32.238:80/web/?a=b", host: "www.example.com"
此屏幕截图中显示了最后一个请求的 SEQ 和 ACK 编号:
源上约 5 秒的空闲计数器与可变的客户端活动之间的竞争条件。第三个涉及的变量当然是网络延迟。
源站上似乎有一个约 5 秒的空闲计时器,而您的客户端需要约 5 秒的时间通过 Nginx 代理发出第二个请求(POST)。如果前者比后者长(包括网络延迟),那么您就没有问题。如果发送客户端请求只需要一点点时间,那么您就有问题了。
你可以看到来自 Nginx 的 POST 和 FIN,ACK 是如何一起发送的:分别在源的 FIN,ACK 之后 2.4 毫秒和 2.6 毫秒。这可能会让您偏离正轨,因为我认为 POST 根本不是对来源的 FIN,ACK 的响应。因为它是在源端的 FIN,ACK 之后 2.4ms 发送的
POST 数据包上的 ACK 号很可能是针对“200 OK”数据包的。在 HTTP 响应之后没有来自服务器端的额外数据,因此来自客户端的任何 ACK 都将 ACKing 相同的数字。
更新:我们现在知道 POST 数据包的 ACK 号增加了 1,因此 Nginx 知道 [FIN,ACK]。进一步的调查表明这很好:如果机器在收到远程端的响应后不打算继续连接,它可能会发送一个请求并以 [FIN,ACK] 结束,远程端将发送回请求的数据并结束继续 [FIN,ACK] 过程。
这并没有改变这样一个事实,即源端决定在空闲 5 秒后关闭连接,从而忽略之后不久出现的 POST 数据包(甚至发回 RST - 尽管不清楚这个 RST 是否会'无论如何都已发送)。
您不必立即返回 FIN,ACK,尤其是在 HTTP 1.1 和持久连接的引入之后。这约 5 秒似乎是原点上的空闲计时器。
这两件事都在这里得到确认:https ://en.wikipedia.org/wiki/HTTP_persistent_connection - 包括 Apache 2.2 或更高版本中默认的 5 秒空闲超时。
建议的解决方案
在不了解您的基础架构的情况下,我无法真正提出解决方案,但粗略地说,您有几个选择:
希望这可以帮助 :)
我认为这是上游服务器socket keep alive超时造成的,socket会被关闭,默认socket.setsolinger不会打开。
我想我们可以让nginx上游服务器keepalive超时。这里另一个作者解决了,请看这个