在 Nginx 文档中,有关于三种不同网络超时的指令,可以为“后端”服务器进行如下配置:
proxy_connect_timeout
定义与代理服务器建立连接的超时时间。需要注意的是,此超时时间通常不能超过 75 秒。
这很容易理解——Nginx 即将连接到上游“后端”服务器,如果它在 X 时间内无法连接,它将放弃并返回错误。服务器无法访问,连接数过多等。
proxy_read_timeout
定义从代理服务器读取响应的超时时间。超时时间仅在两次连续的读取操作之间设置,而不是针对整个响应的传输。如果代理服务器在此时间内未传输任何内容,则连接将关闭。
这也是有道理的——Nginx 已经与“后端”服务器建立了 TCP 连接,现在即将实际发送请求,但服务器需要很长时间来处理,如果需要超过 X 时间,则关闭连接并返回给用户。
我实际上对 Nginx 关闭连接感到很惊讶,我以为它会保留连接但向用户返回错误。每次超时时重新建立这些“后端” TCP 连接听起来很昂贵。
proxy_send_timeout
设置向代理服务器传输请求的超时时间。超时时间仅在两次连续的写入操作之间设置,而不是针对整个请求的传输。如果代理服务器在此时间内未收到任何内容,则连接将关闭。
这个我不太明白。我确实有一个理论,但我希望有人能证实它。我能想到的这个超时的唯一值是请求负载很大(例如带有 JSON 的大型 POST 请求,或者用户想要保存的文档)。将请求传输到“后端”将需要将请求分解为较小的 MTU TCP 段,并将它们作为原始请求的“块”发送。所以从技术上讲,在我们成功将所有块传输到服务器之前,我们实际上并没有发送请求。Nginx 是否测量请求的每个块之间的时间?这就是文档中“写入”的意思吗?一旦请求实际发送,Nginx 将开始测量proxy_read_timeout
?
答案1
TCP/IP 是一种所谓的“流式”数据传输协议。它旨在让通过 TCP/IP 连接读取数据的一方不必关心“段”甚至数据包的大小。实际上,这意味着对等方调用一些传统的“读取”操作来获取远程对等方发送的数据(例如read
(例如,在 Linux 中),不一定一次读取与远程端提供给单个“写入”操作的数据完全相同的数据。TCP/IP 协议实现将始终根据一次传递给“写入”操作的数据生成适当大小的 IP 数据包,而另一端的实现将从这些数据包中重新组装数据;但它不会必须将它们交给具有相同数据边界的一些“读取”客户端应用程序!
例如:A 有 50Kb 的数据要发送以应对每个外部系统事件,数据量如此之大以至于它们无法同时将其全部放入 RAM 中,因此它们以 16Kb 的块发送,这是其发送缓冲区的大小。因此,它们首先发送 16Kb,然后再发送 16Kb,然后再发送 16Kb,最后发送 2Kb。TCP/IP 实现可能会发送这 50Kb,在内部缓冲在 128Kb 缓冲区中(例如内核缓冲区),然后才通过网络发送,这也有其自身的条件。其中一些数据以发送应用程序甚至不知道的方式被碎片化,由于网络条件,它们首先到达另一端,并由那里的 TCP/IP 实现组装并再次放入内核缓冲区中。内核唤醒想要读取数据的进程——读取所有 30Kb。接收方必须决定他们是否期望更多,以及如何理解期望的更多数量——格式TCP/IP 并不关心“消息”或者数据。
这意味着 Nginx 无法知道在基于 Linux 的系统上每次read
调用时它将读取多少客户端请求。
不过,文档proxy_send_timeout
稍微暗示了它的用途(重点是我的):
设置向代理服务器传输请求的超时时间。超时时间仅在两次连续的写入操作之间设置,而不是针对整个请求的传输。如果代理服务器在此时间内没有收到任何内容,连接已关闭。
问题是,由于 Nginx代理请求——意味着请求不起源与之一起——它等待“下游”客户端(向 Nginx 发送请求的连接的远程端,后者作为“代理”,现在希望将请求转发到上游)传输请求的数据,然后才通过上游连接转发(写入)该请求。
我的理解是,如果[在超时时间内]没有从下游收到任何内容,那么代理服务器也不会收到任何东西 - 然后连接就会关闭。
换句话说,如果下游在指定的时间内没有发送任何东西proxy_send_timeout
,Nginx 将关闭与上游的连接。
例如,假设 Web 浏览器向 Nginx 发送请求。Nginx 在时间 A 读取第一部分。假设它将请求代理到某个上游,它会打开与该上游的连接并通过上游连接套接字传输(写入)从浏览器收到的内容。然后,它只需等待从浏览器读取更多请求数据部分 — 如果相对于时间 A 的某个超时 X 后下一部分仍未到达,它将关闭与上游的连接。
请记住,这并不一定意味着它将关闭与 Web 浏览器的连接 —— 它肯定会为请求返回一些 HTTP 错误状态代码,但 Web 浏览器连接的生命周期受一组不同的条件控制proxy_send_timeout
—— 后者仅涉及 Nginx 与上游的连接。