8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

Chrome 开发者工具中未报告僵尸 SSE 连接

jpoppe 2月前

31 0

背景我正在构建一个使用 HTTP/1.1 供内部使用的 Web 应用程序。它没有 TLS,因此不支持 HTTP/2,也不支持 HTTP/3 或 QUIC(这些新协议可以解决问题,但

背景

我正在构建一个使用 HTTP/1.1 供内部使用的 Web 应用程序。它没有 TLS,因此不支持 HTTP/2,也不支持 HTTP/3 或 QUIC(这些新协议可以解决问题,但目前无法访问)。

客户端使用:

  • 一些静态资源
  • 一些 API 端点(都非常快,都使用 Fetch API)
  • 2 个并发且长时间运行的服务器发送事件流

所有请求均由 nginx 提供服务,但看起来我的问题与它无关。

问题

有时,刷新页面后,某些请求会停滞数秒。这些请求是由 Chrome 停滞的,即 Chrome 决定在导航或 JS Fetch API 发起请求后不立即发送 HTTP 请求。DevTools 没有给出任何提示来了解为什么会发生这种情况。

Chrome Dev Tools screenshot

我尝试排除一些常见问题:

  • 更高优先级的请求 :它是一个 Fetch,因此不应被视为低优先级请求;此外,没有其他获得优先处理的请求。
  • 服务器工作人员 thereads :我没有看到任何错误消息,但很明显该请求并未离开 Chrome。
  • QUIC :我在 HTTP/1.1 上运行,不是 HTTP/2,不是 HTTP/3,不是 QUIC。
  • 用于缓存的磁盘空间分配 在 Fetch 中 {cache: 'no-store'} 指定
  • Chrome 每个源规则的最大 TCP 连接数为 6 个 :这实际上是我第一次检查。开发工具中显示的 SSE 连接数从未超过 2 个;停滞的请求似乎不会在另一个请求完成后启动。我没有看到任何其他可能持有 TCP 套接字的待处理请求。没有打开其他选项卡。剧透:这个限制实际上是启用的。

经过一些检查后,我花了一些时间将 nginx 配置为 Connection: close 在所有响应中始终发送 HTTP 标头,这样客户端就不会尝试对即将到来的请求重用相同的 TCP 连接。我认为 Chrome 乐观地拖延重用 SSE 请求的 TCP 连接,假设它会在短时间内释放。这对于防止连接重用是有效的,但这不是问题的根源——Chrome 中没有这种乐观的拖延,这是有原因的。

进入Wireshark

并发 SSE 连接实际上已达到 6 个连接的限制。

重点是:这些 SSE 连接未被任何选项卡使用,未显示在 Dev Tools 中,并且底层 TCP 保持打开状态。事实上,Wireshark 允许我查看 TCP 4 次终止握手:

  • 启动很晚,过了很久才刷新页面,SSE才放弃
  • 它发生在停滞的请求离开之前。
  • 由 Chrome 发起

更准确地说,捕获的 TCP 数据包的序列是(见下面的截图):

  • (流 1)chrome > 服务器:FIN,ACK (僵尸 SSE 连接)
  • (流 1)服务器 > chrome:ACK
  • (流 13)chrome > 服务器:SYN
  • (流 13)服务器 > chrome:SYN,ACK
  • (流 13)chrome > 服务器:ACK
  • (流 13)chrome > 服务器:GET... (停滞了很长时间的请求)

上述事件发生在 1.2 毫秒内,因此在我看来,Chrome 决定暂停流 13 的请求,直到流 1 连接的客户端>服务器端关闭。经过进一步检查,我还可以确认关闭流 1 确实释放了 第六个连接 (打开的 TCP 连接数从 6 个减少到 5 个,允许为暂停的请求打开新连接)。(我省略了以下 ACK 和上述数据包之后流 13 的完全终止)。

Wireshark screenshot

问题

为什么 SSE 连接仍然处于打开状态?

如何避免僵尸 SSE 连接?

为什么开发工具中没有报告僵尸连接?

帖子版权声明 1、本帖标题:Chrome 开发者工具中未报告僵尸 SSE 连接
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由jpoppe在本站《sockets》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 如何避免僵尸 SSE 连接?

    解决方案:卸载之前明确关闭服务器发送事件源:

    const sse = new EventSource(...)
    const handler = () => {
        sse.close()
    }
    window.addEventListener('beforeunload', handler)
    window.addEventListener('unload', handler)
    

    我通过反复注释处理程序将其作为解决方案隔离开来,并确认当且仅当我明确关闭 SSE 连接时,它们才会在页面重新加载时始终关闭。

    但是, HTTP/1.1 上 SSE 的已知限制 在我看来并不是在页面卸载后留下僵尸 SSE 连接的好借口。DevTools 报告 SSE 请求已完成(瀑布列中的条形图不会继续增长),而它实际上仍然在与页面分离的情况下运行。

返回
作者最近主题: