Better

Ethan的博客,欢迎访问交流

WebSocket 可以替代 HTTP 吗

最近需要实现一个服务端推送的场景,对 WebSocket 的使用存在一些纠结。

HTTP vs WebSocket

websocket 也可以实现 HTTP 的通信功能,那么具体情况到底该如何选择呢?

  • 可能通用的原则
    • 默认情况下应该使用传统的 HTTP
    • 根据具体情况,说服自己的 API 应该使用 websocket
  • 相关对比
    • 双工:HTTP 半双工,同时只能向一个方向传输。websocket 全双工,双方都能同时发送和接受数据
    • 消息模式:Request-response 与 Bi-directional(双向)
    • websocket 支持消息推送
    • 开销:HTTP 每个请求都会一定开销,websocket 主要在建立和维持连接上,每个消息的开销较小
    • 边缘缓存:HTTP 支持缓存,websocket 则没有
  • 适用 HTTP 的场景
    • 检索资源:查看比赛结果(HTTP)vs 查看比赛实时比分(websocket)
    • 高度可缓存资源
    • 幂等性和安全性:能更好的适应通信失败。因为 HTTP 具有广泛的安全性和幂等性期望。WebSocket 协议将这些问题留给了消息层设计(这意味着没有广泛的行业标准)。
    • 错误场景:HTTP 允许响应去描述错误信息,比如请求、资源,或者提供状态信息来区分不同的成功场景。WebSocket 协议只对影响连接建立的错误场景提供支持,一旦请求建立和消息交换,任何额外的错误场景必须在消息传递层设计中解决。
    • HTTP 支持同步事件
  • 使用 websocket 场景
    • 快速的反应时间:比如实时聊天
    • 实时更新:实时更新资源的状态
    • 点对点消息
    • 高频消息传递:websocket 只在建立连接时有较大开销,后续消息传递没有额外开销。虽然 HTTP 1.1 允许多个请求复用一个连接,通常会有小的超时时间用于控制资源消耗。websocket 避免了建立了解和发送 HTTP 头部信息的开销,带来大的性能提升。

如果仅从功能角度考虑,websocket 提供了让人心动的服务端推送能力,对于实时性要求比较高的场景十分有用。但设计上不如 HTTP 简单、完善,HTTP 半双工通信机制足够简单,使用的编码过程中更好控制数据状态。与此同时 HTTP 还提供了缓存机制,幂等性和安全性能更好的适应通信失败,对于错误场景更加规范,而这些都是 websocket 所没有的,必须通过良好的设计去解决。

socket.io

本次实现并没有直接使用原生 websocket,而是使用了 socket.io 这个优秀的封装实现。

socket.io 特性

  • 自动降级:HTTP 长轮询
  • 自动重连
  • 包缓冲区
  • 确认机制,支持 timeout 设置
    • emit 函数最后传递一个回调,服务端确认收到后,回调用它
    • on 函数回调函数 payload 中最后是一个函数,可手动调用它表示确认收到,支持传参,默认会自动无参调用(maybe)
    • 通过 ack 特性,可实现类似 request-response API
  • 广播
  • 多路复用:支持多 namespaces

关于断开连接

  • 即使是在一个稳定的互联网连接上,断开连接也是常见的和预期的
  • 由于网络原因,发送完 emit 后,连接断开,自动重连,如果服务端不做特殊处理,是收不到预期的消息的,能否巧妙利用 Room 群发特性,将 Room 设置为 userId 的方式解决?
  • 重要提示:当中间件被执行时,Socket 实例实际上并没有被连接,这意味着如果连接最终失败,不会触发断开事件。

sessionId and socketId,连接建立具体步骤

  1. 握手成功后,服务端会返回 sessionId,这在后面并发的 HTTP 交流都会被用到
  2. 使用 sessionId 请求建立连接,这里可提交需要的 auth 信息
  3. 通过后,请求获取 socketId
  4. 建立 websocket 连接
  5. 第一个 HTTP 长轮询请求,一旦建立 WebSocket 连接就关闭

socket.io redis adapter

  • 服务于多个 servers 场景,负责将事件广播给所有或一部分客户端
  • 即使有了 adapter,当使用多节点服务以及 HTTP 长轮询机制时,sticky sessions 仍然需要

资料



留言