我正在运行一个相对简单的 VPS(Media Temple (ve)),上面有几个基于 PHP 的网站,以及(最终)几个节点服务器。为了启用 WebSockets 支持,我在端口 80 上使用 HAProxy,该端口路由到 nginx 或特定的 Node 进程。
不过,我最近遇到了一个问题,在大约 24 小时内,我的服务器达到了允许的最大打开 TCP 连接数(numtcpsock
在 Parallels Power Panel 中,设置为 1,000)。单独运行 nginx 不会导致此问题,并且我目前没有活动的 Node 后端服务器。Nginx 通过 UNIX 域套接字连接到 PHP(同样,单独使用 nginx 不会出现此问题)。有什么想法可能导致此问题?我的配置:
global
## 00-base
maxconn 500
nbproc 2
defaults
## 00-base
mode http
frontend all
## 00-ports
bind 0.0.0.0:80
## 10-config
timeout client 86400000
default_backend nginx
backend nginx
## 00-timeouts
timeout http-keep-alive 5000
timeout server 10000
timeout connect 4000
## 10-servers
server main localhost:8000
提前致谢!
更新:经过一番lsof
研究,我能够确定 90% 以上的开放 TCP 套接字确实归 HAProxy 所有,并且绝大多数处于CLOSE_WAIT
或FIN_WAIT2
状态。这是 HAProxy 错误吗?这看起来像是某种文件描述符泄漏,除非是我的配置错误。
更新2:我注意到lsof
输出中有一个模式。在我看来,发生的事情是 nginx 正在关闭与 HAProxy 的内部连接,但在 HAProxy 正式关闭它之前,它会尝试关闭与客户端的外部连接(将其放入FIN_WAIT2
)。由于 FIN 永远不会到来,因此 nginx 和 HAProxy 之间的连接将CLOSE_WAIT
永远保持。现在唯一的问题是:为什么会发生这种情况?
答案1
问题是由于您的超时时间过长造成的。超时时间为 24 小时,同时连接数限制为 1000,显然客户端会以不正当的方式断开连接。请使用更合理的超时时间,从几分钟到最多几小时,在互联网上使用 1 天的超时时间确实毫无意义。正如 DukeLion 所说,系统正在等待 haproxy 关闭连接,因为 haproxy 没有收到来自客户端的关闭。
Haproxy 在 TCP 和 WebSocket 的隧道模式下工作,它遵循通常的四向关闭:
- receive a close on side A
- forward the close on side B
- receive the close on side B
- forward the close on side A
在您的例子中,我假设 A 端是服务器,B 端是客户端。因此,nginx 在一段时间后关闭,套接字进入 CLOSE_WAIT 状态,haproxy 将关闭转发给客户端,此套接字进入 FIN_WAIT1 状态,客户端确认,将套接字传递到 FIN_WAIT2 状态,然后什么也没发生,因为客户端已经消失,这在网络上很常见。您的超时意味着您希望这种情况持续 24 小时。
24 小时后,您的会话将在客户端开始超时,因此 haproxy 将终止它们并将关闭转发到 nginx 端,从而将其也删除。但显然您不希望发生这种情况,WebSocket 的设计使得空闲连接可以透明地重新打开,因此没有理由将空闲连接保持打开状态 24 小时。没有防火墙会一直保持它!
答案2
很多套接字处于 CLOSE_WAIT 状态对服务器来说是极其糟糕的。当内核等待用户空间软件接受套接字关闭时,就会出现此状态。如果很多套接字长时间处于此状态 - 这意味着使用它的软件没有响应。通常,处于此状态的套接字会消耗相对大量的内核 CPU 时间。
我认为您情况下的 CLOSE_WAITs 是 FIN_WAIT2 的次要部分 - HAproxy 正在等待客户端连接关闭,然后在此之后关闭与 NGINX 的连接。
FIN_WAIT2 是等待另一方确认套接字关闭的状态。出现很多这样的情况并不是很糟糕,但它可能表示网络拥塞或严重丢失。
您可以尝试nolinger
使用 haproxy 选项来更快地关闭连接。但要小心,这会破坏保证交付的 tcp 机制。