我正在构建一个 Web 应用程序,供内部使用 HTTP/1.1。 它没有 TLS,因此不支持 HTTP/2,也不支持 HTTP/3 或 QUIC(这些新协议可以解决问题,但目前无法访问)。
客户使用:
所有请求均由 nginx 提供服务,但看起来我的问题与它无关。
有时,刷新页面后,某些请求会停滞很多秒。 Chrome 会停止请求,即导航或 JS Fetch API 发起请求后,Chrome 决定不再发送 HTTP 请求。 DevTools 没有给出任何提示来理解为什么会发生这种情况。
我尝试排除一些常见问题:
{cache: 'no-store'}
,但没有成功。经过一些检查,我花了一些时间将 nginx 配置为始终在所有响应中发送
Connection: close
作为 HTTP 标头,以便客户端不会尝试为即将到来的请求重用相同的 TCP 连接。
我认为 Chrome 乐观地停止重用 SSE 请求的 TCP 连接,假设它会在短时间内释放。
这可以有效地防止连接重用,但这并不是问题的原因——Chrome 中没有如此乐观的停滞,这是有充分理由的。
进入 Wireshark
并发 SSE 连接实际上已达到六个连接限制。
重点是:这些 SSE 连接没有被任何选项卡使用,没有显示在开发工具中,并且底层 TCP 保持打开状态。事实上,Wireshark 允许我看到 TCP 4 路终止握手:
更准确地说,捕获的 TCP 数据包的顺序是(见下面的屏幕截图):
上述事件发生在 1.2 毫秒内,因此我很清楚 Chrome 决定推迟流 13 的请求,直到流 1 连接的客户端>服务器端关闭后。经过进一步检查,我还可以确认关闭流 1 实际上释放了当前打开的第六个连接(打开的 TCP 连接数从 6 减少到 5,允许为停滞的请求打开新连接) 。 (我省略了以下 ACK 以及上述数据包之后流 13 的完全终止)。
为什么SSE连接仍然打开?
如何避免僵尸 SSE 连接?
为什么开发工具中没有报告僵尸连接?
如何避免僵尸 SSE 连接?
解决方案:在卸载之前显式关闭服务器发送的事件源:
const sse = new EventSource(...)
const handler = () => {
sse.close()
}
window.addEventListener('beforeunload', handler)
window.addEventListener('unload', handler)