本文从技术选型分析到网站实战,将项目Ai网站中流式渲染的技术进行拆解,对比分析SSE和websocket的实现。

1. SSE简介

SSE(Server-Sent Events)译为服务器推送事件,通过EventSource接口实现服务器推送通信。与webSocket不同的是,SSE基于http连接,为单向通信,在单向推送场景下(各大AI网站)得到很好的应用。一个 EventSource 实例会对 HTTP 服务器开启一个持久化的连接,以 text/event-stream 格式发送事件,此连接会一直保持开启直到通过调用 EventSource.close() 关闭。客户端开启EventSource连接后通过监听特定的事件(如notice,update,meessage)来处理相关的逻辑。

EventSource 的特点

  1. 单向通信:服务器向客户端推送消息,客户端不能向服务器发送消息。
  2. 文本流:数据以文本形式发送,通常是 UTF-8 编码。
  3. 自动重连:如果连接中断,浏览器会自动尝试重新连接。
  4. 事件命名:可以为消息指定不同的事件类型,客户端可以根据事件类型选择性地处理消息。

sse也有一定的缺陷:

  • 不支持双向通信。
  • 不支持二进制数据传输。
  • 兼容性存在问题,不支持 IE 浏览器。
  • 单个浏览器最大连接数限制

    当不使用 HTTP/2 时,服务器发送事件(SSE)受到打开连接数的限制,这个限制是对于浏览器的,并且设置为非常低的数字(6),打开多个选项卡时可能会特别痛苦。在 Chrome 和 Firefox 中,这个问题已被标记为“不会修复”。这个限制是每个浏览器和域名的,这意味着你可以在所有标签页中打开 6 个 SSE 连接到 http://www.example1.com,以及另外 6 个 SSE 连接到 http://www.example2.com(来源:Stackoverflow)。当使用 HTTP/2 时,最大并发 HTTP 流的数量是由服务器和客户端协商的(默认为 100)。

const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "text/event-stream",
    },
    body: JSON.stringify(data),
  }).catch((err) => {
    console.log("err报错了", err);
  });
  // 获取 ReadableStream 并创建读取器
  const reader = response.body.getReader();

}
使用TextDecoder 解码:

EventSource发送来的是utf-8编码的信息,通过reader.read()得到的value为字节流,我们还需要使用TextDecoder 解码器器将字节流作为输入,并提供码位流作为输出,这个过程可以称为翻译。

  const decoder = new TextDecoder();
  // 持续读取流数据
  while (true) {
    const { done, value } = await reader.read();
    if (done) {
      reader.releaseLock();
      break;
    } // 流结束
    console.log("字节流", value);
    const chunk = decoder.decode(value);
    console.log("解码后数据为", chunk);
  }

在AI网站上,我们通常会看到AI回答的内容有列表,有代码块,还有表格等等,通过观察他们的数据格式,通过引入markdown库来对返回信息进行解析,再实时更新页面,这就有了页面打字机和丰富的排版效果。

备注:SSE需要注意通信格式:每条信息后面需以\n\n结束(SSE协议规定的)。 EventSource 中,发送数据的行以冒号开头表示注释行。这些行是发送给客户端的非事件数据,浏览器会忽略它们,但它们有助于保持连接活跃:

// 保持连接,避免断开
    setInterval(() => {
      res.write(': keep-alive\n\n');
    }, 15000);

EventSource 发送数据时需要以特定的关键字开头,如 data、event、id、retry 和 :(注释行)

EventSource本质上还是Http协议,所有的Http协议都是用Http字段来跟浏览器谈心的,所以必须设置这几个header:status、content-type、cache-control、connection,不然浏览器不认识它。

 HTTP/1.1 协议默认开启持久连接(persistent connection),这意味着同一个 TCP 连接可以被重用来发送和接收多个 HTTP 请求和响应。然而,如果连接在一段时间内没有活动,某些中间网络设备(如防火墙、代理服务器)或客户端本身可能会认为连接已经闲置太久,从而关闭连接。所以需要定时发送心跳让浏览器别消灭它:res.write(': keep-alive\n\n')。

2.websocket

首先对比一下SSE 和 WebSocket 流式输出的特性:

SSE (Server-Sent Events) WebSocket
通信方向 单向 (Server → Client)  双向 (Server ↔ Client)
协议 HTTP/1.1 + EventSource WebSocket 协议
连接方式 基于 HTTP 长连接  需要单独 升级协议
消息格式 纯文本(也支持 JSON) 二进制 / JSON / 文本
断线重连 浏览器自动重连 需要手动实现重连逻辑
服务器推送 天然支持(适合 AI 生成式内容 需要额外实现
服务器压力 轻量级(基于 HTTP/1.1) 服务器需要维护更多连接
浏览器支持 所有现代浏览器支持 需要 支持 WebSocket
适用场景 AI 流式输出、日志推送、股票行情等 实时聊天、双向交互、多人协作等

通过上面的对比,不难得知SSE更适合Ai生成式项目,但为什么要使用WebSocket?与 SSE 相比,WebSocket 具有以下特点:

  1. 支持双向通信 , 客户端可以随时向服务器发送消息,而不仅仅是等待 AI 回复。
  2. 更低的连接开销 ,WebSocket 连接后,数据传输比 SSE 高效,适用于高并发场景。
  3. 支持二进制数据,可以更灵活地发送 JSON、二进制流,适配 AI 复杂交互。
  4. 连接状态可控 ,可以手动断开连接、重连、主动停止 AI 生成。

综合以上,如果你的项目需要支持更多的参数和互动效果,比如手动终止以及高并发场景Websocket是不错的选择😌。

    Logo

    火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

    更多推荐