WebSocket 和 SSE 穿过 nginx 要配什么?空闲多久会被断开?client 断开后上游多久发现?长连接会占掉多少网关连接?这篇用 websockets 17.2 和 nginx 1.31.6 逐项量出来。
配置 | 结果 | 往返中位数 | p99 ------------------+---------------------------------------------------------------+---------+-------- (a) 只有 proxy_pass | InvalidStatus: server rejected WebSocket connection: HTTP 426 | - | - (b) 只转发 Upgrade 头 | InvalidStatus: server rejected WebSocket connection: HTTP 426 | - | - (c) 完整写法 | 握手成功(101) | 0.18 ms | 0.41 ms (d) 直连上游 | 握手成功(101) | 0.06 ms | 0.09 ms
要同时转发 Upgrade 和 Connection: upgrade,并用 HTTP/1.1;否则上游拒绝。
客户端 | 结果 --------------------+-------------------- (a) 不发任何东西 | 2.0 s 被关闭(关闭码 1006) (b) 每 0.5 s 一个 ping | 7 秒内一直保持 (c) 每 1.5 s 一个 ping | 7 秒内一直保持 (d) 每 2.5 s 一个 ping | 2.0 s 被关闭(关闭码 1006) proxy_read_timeout 管的是“两次从上游读到数据之间的间隔”:WebSocket 的 ping 帧由 client 发给上游,上游回 pong,pong 到了才算 nginx 读到数据,所以心跳间隔要比 proxy_read_timeout 短(这里 0.5 s 和 1.5 s 都小于 2 s,2.5 s 大于)
proxy_read_timeout 管两次读到数据的间隔,心跳间隔要比它短。
路径 | client 关闭之后,上游发现写失败用了 ----------------------------------------+---------------------- 直连上游 | 0.02 s 经过 nginx(默认) | 0.23 s 经过 nginx + proxy_ignore_client_abort on | 0.44 s
client 断开后,数据在流动时上游一两个事件内就会发现,与 ignore_client_abort 的差别约一个事件。
路径 | 尝试建立 | 成功握手并保持 | 失败的类型 | error.log: not enough
--------------------------------+------+---------+----------------------------------------------------+----------------------
经过 nginx(worker_connections 64) | 45 | 31 | {'ConnectionClosedError': 11, 'InvalidMessage': 3} | 1
直连上游 | 45 | 45 | {} | - 每个长连接在网关上占两条连接。