使用CDN对WebSocket的影响及解决方法

给本博客的 Wekan 面板套了一层 CDN,在近两个月的使用中发现了诸多问题,而大多数问题是由于套 CDN 导致WebSocket连接出现问题造成的。

起因

本博(梓喵出没)有个内部的 Wekan 面板,它使用 Docker 部署,并通过梓喵出没主站的 Nginx 反代其内部服务。

前前后后用了两个月,主要功能正常使用,但总是有一些小问题让人烦恼,比如在某些情况下无法切换语言,又或者在版本升级后打开首页面板无限刷新等。

经过仔细排查,发现 WebSocket 连接会无故出现断连、无法连接、连接后瞬间断开等问题。

而后,本地无法复现此问题,最终问题锁定在 CDN 和 WebSocket 上。

找问题

1.WebSocket 基础知识

首先说一下 WebSocket 的基本知识:WebSocket 通过 HTTP 协议协商和握手,在握手开始时,客户端有一些新 Header 参数:

Connection: Upgrade
Upgrade:websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: JfusUfenX6BBstD7/+NipA==

关于ConnectionUpgrade字段的作用,RFC 7230 有如下描述:

发送后,服务器返回 101 状态码(Switching Protocols),响应头中同样包含ConnectionUpgrade,并多了一个Sec-WebSocket-Accept字段:

Connection:Upgrade
Sec-WebSocket-Accept:HZjQkN/yAUHpfAgfnxqHEddUgyQ=
Upgrade:websocket

Sec-WebSocket-Accept用作握手的确认校验,它是由客户端请求中的Sec-WebSocket-Key参与计算而来的(算法参考 RFC6455),客户端校验 Sec-WebSocket-Accept 后,握手完成。

握手完成后,这条 TCP 连接将保持长连接,应用层解析字节流时的协议由 HTTP 协议变为 WebSocket 协议,双方通过这条 TCP 连接发送消息帧。

在 Firefox 的开发者工具中,可以看到消息帧:

2.腾讯云 CDN + WebSocket

腾讯云声称自己的 CDN 支持WebSocket,且WebSocket内容为动态内容,不会被缓存,必定回源,当无消息帧持续 10 秒时主动断开。

然而,经过一波操作,发现管理控制台中一旦开启默认的“所有内容缓存 30 天”规则,即出现WebSocket连不上或连上就断连等各种奇怪的情况。

我尝试将此项改为 0 天,并将静态文件匹配规则独立出来:

经过测试,在 Chromium 内核的浏览器(Chrome、Edge)中,WebSocket 连接正常。

使用 Firefox 时,WebSocket建立连接直接失败,在握手的响应头中,Connection项为close;而通过 Host 直接访问源站, Firefox 是正常的。

Wekan 面板做了处理,当WebSocket无法连接时,会使用 HTTP 协议获取数据,因此即便是 Firefox 用户,也算能正常使用了。

总结

所有问题都是由 CDN 起,解决方法除了取消 CDN、WebSocket API 独立域名之外,就是尝试修改缓存设置。实在不行就像 Wekan 开发团队那样,准备 WebSocket + HTTP 两套 API 适配器,哪个能用就用哪个。

梓喵出没博客(azimiao.com)版权所有,转载请注明链接:https://www.azimiao.com/6670.html
欢迎加入梓喵出没博客交流群:313732000

我来吐槽

*

*