websocket 实战注意事项
shell
# 使用 websocket 的过程中发现了一个事
前后端 websocket 通信,是多个功能统一使用一个 socket 连接,通过 socket 派发的不同事件来区分功能,而不是像 http 那样,每一个功能,建立一个 http 连接
HTTP 短连接(接口)
每请求一次功能 → 建立一次 TCP 连接
请求结束 → 立刻断开
不同业务:用户、订单、消息、文件,各自单独请求、单独建连
模式:一问一答、无状态、每次都要重新请求
WebSocket / Socket.IO 长连接
项目全局只建立 1 条 Socket 长连接
连接一次,全程复用,不断开
不同业务(聊天、通知、大屏、心跳、订单推送)
靠「自定义事件名」区分:
chat_msg 聊天消息
order_change 订单状态
system_notice 系统通知
heart_beat 心跳检测
形象对比
HTTP 新增一个功能 = 新加一个接口地址,新建一次请求连接
Socket 新增一个功能 = 只新增一个事件名称,复用同一条长连接,不新建连接
代码示例
前端代码
const socket = io('后端地址') # 全局就 1 个 socket 实例,1 条长连接
## 不同功能,用不同事件区分
socket.on('chat_msg', fn) # 聊天功能
socket.on('order_push', fn) # 订单推送
socket.on('notice', fn) # 系统通知
后端代码
@SubscribeMessage('chat_msg') # 聊天事件
@SubscribeMessage('order_push') # 订单事件
# 还有,另外一个事儿,前后端都没用到 ws:// wss:// 相关协议
因为:WebSocket/Socket.IO 基于 HTTP 协议完成初次握手,握手成功后,自动协议升级(整个过程在浏览器上可见 ws://,业务代码无感知)
另外 WebSocket/Socket.IO 依附于后端主服务,与后端服务共用一个端口,不是独立服务;
前端填写:
线上 HTTPS 项目:填 https://xxx
本地开发 HTTP:填 http://xxx
握手成功后,自动协议升级:
https 环境 → 自动升级为 wss:// 加密长连接
http 环境 → 自动升级为 ws:// 明文长连接
全程
不用前/后端手动写 ws:/// wss://
# 在 websocket 实战开发中一般不使用原生的 websocket API 而是使用封装好的库
前端 vue3:pnpm add socket.io-client
后端 nestjs:pnpm add socket.io
# 问题一:整个项目只能有一个 WebSocket 请求吗?
不是,可以有多个,但是 中小型项目(推荐),只建立 1 个 WebSocket 连接,过多路复用处理所有消息
# 问题二:什么时候需要用到多个?
如果业务之间需要隔离(如一个断了不影响另一个),可以按域名拆分:
wss://chat.example.com → 聊天专用
wss://push.example.com → 推送专用
wss://data.example.com → 数据流专用
# 问题三:浏览器限制是多少?
Chrome、Firefox、Safari 都默认单域名最多 6 个并发 TCP 连接,Edge 可配到 256
一个页面内,所有 XHR、fetch、图片、WebSocket 加起来,往同一个域名最多同时开 6 个 TCP 连接。
注意:TCP 连接数不是 http 请求数,TCP 连接相当于高速通道,而 http 请求相当于过往车辆,如果车辆过多就要排队了
# 问题四:服务器能撑多少?
调优后可到 50 万+ 空闲连接,
# 方案:nginx 负载均衡横向扩展,同时启动多个后台服务实例,一台代理一万
负载均衡 + Sticky Session(ip_hash策略)
操作系统调优 - 支撑单机5万+连接
如果用Node.js实现,建议将目标设定为一个服务器处理5-10万连接,然后横向扩展。一个服务器 16核CPU + 32GB运行内存,
负载均衡方案,不是都用 nginx
第一层:nginx 做负载均衡代理到 10 台服务器上
第二层:每台服务器上的Nginx再做负载均衡,把流量分发给本机的 5 个NestJS进程。 每个进程承担 10000 到 20000 的并发数
# 问题五:最大瓶颈是什么?
连接抖动(频繁握手)> 内存 > CPU > 带宽