最近在折腾服务器配置的时候,碰到了个挺坑的问题,估计有不少兄弟也遇到过:明明反向代理配置得好好的,缓存命中率也相当漂亮,结果为了支持 WebSocket 连接,把 supports_websockets 设置为 true 之后,缓存命中率直接呈断崖式下跌。

这到底是为啥?难道开启 WebSocket 和缓存是“鱼与熊掌不可兼得”?今天就把这个问题掰开了揉碎了讲清楚,顺便给点解决方案。

一、现象重现:从 90% 到 0% 的暴跌

Nginx 反向代理缓存命中率下降监控曲线图

缓存命中率暴跌的监控示意图

通常我们配置 CDN 或者 Nginx 反向代理缓存(比如 Proxy_cache)时,静态资源如 CSS、JS、图片很容易被缓存。但当你试图支持长连接应用(比如实时聊天、在线协作)时,往往会参考网上的教程,添加类似 proxy_set_header Upgrade $http_upgrade; 以及 proxy_set_header Connection "upgrade"; 的配置,或者在某些面板里勾选“支持 WebSocket”。

此时,诡异的事情发生了:原本的静态资源请求开始大量穿透到源站,缓存命中监控曲线直接趴窝。CPU 和带宽压力瞬间飙升,让人摸不着头脑。

二、技术复盘:为什么会冲突?

这就得从 HTTP 协议和 WebSocket 的握手机制说起了。

Nginx WebSocket 路由配置代码段

Nginx 中将 WebSocket 与普通流量分离的配置示例

1. 缓存的判定依据 绝大多数反向代理服务器(Nginx、Varnish 等)在进行缓存判定时,有一套严格的规则。比如,请求方法必须是 GET 或 HEAD,URL 必须一致,而且通常要求头信息中不包含特定的 Cookie 或 Authorization 字段。

2. WebSocket 的握手特征 WebSocket 连接建立时,客户端会发送一个带有特殊 Header 的 HTTP 请求:

  • Upgrade: websocket
  • Connection: Upgrade

当你全局开启了“支持 WebSocket”功能,很多配置模板的做法是:检测到 Upgrade 头时,直接跳过缓存逻辑,建立隧道连接。

盲点在于: 某些配置不够严谨的脚本或反向代理逻辑,可能会误判。比如,它可能将对 ws://wss:// 的配置策略错误地应用到了普通的 HTTP 请求上,或者在处理混合内容(同一域名下既跑 WebSocket 又跑普通 API)时,因为共享了同一个 location 块或 Upstream 配置,导致所有进入该块的请求都被强制标记为“非缓存”,或者因为引入了 $http_upgrade 变量导致缓存 Key 发生了不可预测的变化,进而导致无法命中缓存。

另外,WebSocket 连接通常是长连接,一旦建立,流量就在 TCP 层面传输了,代理层(如 Nginx)如果配置不当,可能会因为无法识别后续的数据包是“可缓存的 HTTP 响应”还是“WebSocket 帧”,从而干脆关闭了缓存功能以防万一。

三、排查与解决方案

既然知道原理了,那怎么解决?核心思路就是隔离流量精确配置

1. 流量分离(最佳实践)

永远不要把 WebSocket 流量和普通的静态资源/API 流量混在同一个 location 块里处理,除非你的技术栈(如 V2Ray、Nginx)专门针对混合模式做了优化。

Nginx 配置示例:

# 专门用来处理 WebSocket 的路径
location /ws/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    # WebSocket 不需要缓存,也不存日志
    access_log off;
}

# 专门处理静态资源或 API
location / {
    proxy_pass http://backend;
    proxy_cache my_cache;
    proxy_cache_valid 200 1h;
    # 这里千万不要盲目复制 WebSocket 的 Upgrade header
    # 因为普通的 HTTP 请求不需要它,而且加上去可能导致缓存失效
    add_header X-Cache-Status $upstream_cache_status;
}

2. 避免全局污染

如果你使用的是 Cloudflare 或第三方 CDN,不要为了支持 WebSocket 就把“开启 WebSocket”的选项应用于整个站点。通常建议专门为 WebSocket 的子域名或路径开启,而主域名保持默认的 HTTP 强缓存策略。

3. 检查缓存 Key

如果你必须在同一个 Location 处理,请务必检查你的 proxy_cache_key 定义。确保没有把 $http_upgrade 变量包含在 Key 的计算逻辑中,否则普通请求因为没有这个头,而缓存存的是有头的请求,自然对不上号。

四、总结

supports_websockets = true 本身没错,错的是配置范围的扩大化。反向代理缓存和 WebSocket 对连接状态的要求一个是“无状态、可复用”,一个是“有状态、长连接”,二者的路由策略必须分开。

如果你的服务器最近莫名变卡,且缓存命中率低,先去检查一下 Nginx 或反向代理配置,是不是不小心把 WebSocket 的“特权”普及到了普通请求头上。分离它们,世界瞬间清净了。

标签: none

AI Skills Smart Station on Nick Launches

评论已关闭