AI流式交互:SSE与WebSocket技术选型

前言

在开发 AI 对话产品(如 ChatGPT、豆包)时,如何实现那种"一个字一个字蹦出来"的流畅打字机效果?SSE(Server-Sent Events) 凭借其轻量级、基于 HTTP 的特性,成为了目前流式输出的主流方案。本文将从基础概念到企业级实战,带你彻底掌握 SSE。

一、 核心概念:什么是 SSE?

SSE (Server-Sent Events) 是一种基于 HTTP 协议的服务端推送技术。它利用 HTTP 的长连接特性,在客户端与服务器之间建立一条持久化通道,服务器通过这条通道实现数据的实时单向推送。一个基于SSE的聊天返回接口如下:

1. 核心特点

  • 单向通信:仅支持服务器 → 客户端的数据推送。
  • 数据格式:原生仅支持文本(默认 UTF-8 编码)。
  • 自动重连:浏览器原生支持断线自动重连。
  • 传统限制 :原生 EventSource 仅支持 GET 方法,无法满足 AI 场景中复杂的 POST 参数需求。

二、 基础用法:原生 EventSource

EventSource为浏览器提供的原生接口,适用于简单的单向推送场景。具体使用如下:

json 复制代码
// 1. 创建实例连接接口
const eventSource = new EventSource('http://localhost:3000/sse');

// 2. 监听消息
eventSource.onmessage = (event) => {
  console.log('收到服务器数据:', event.data);
};

// 3. 监听自定义事件 (需与后端约定事件名)
eventSource.addEventListener('custom-event', (event) => {
  console.log('自定义事件消息:', event.data);
});

// 4. 状态监听
eventSource.onopen = () => console.log('SSE 连接已建立');
eventSource.onerror = (err) => console.error('SSE 出错:', err);

// 5. 主动关闭
// eventSource.close();

注意事项:原生 EventSource 仅支持 GET 方法,咱不能在向后端发送聊天内容时总是将参数拼在地址里面吧


三、 企业级实战:突破限制的 Fetch 方案

在 AI 场景下,用户的问题和上下文通常需要通过 POST 传输,此时原生 EventSource 就显得力不从心。推荐使用微软开源的 @microsoft/fetch-event-source 库。

1. 该方案的优势

  • 支持 POST 及其他所有 HTTP 方法。
  • 支持自定义请求头(Headers) (方便携带 Token 等鉴权信息)。
  • 更好的错误重试策略和超时配置。

2.代码封装

以下是我在不同项目中调用流式接口时封装的请求函数:

typeScripyt 复制代码
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { ElMessage } from 'element-plus'

let controller //用于中止请求

//请求流式接口数据
export const requestStream = (url, headers, data, handleMessage) => {
  controller = new AbortController()
  fetchEventSource(url, {
    method: 'POST',
    signal: controller.signal,
    headers: headers,
    body: JSON.stringify(data),
    openWhenHidden: true, // 允许在页面隐藏时继续发送请求
    async onopen(response) {
      //建立连接的回调
      console.log('建立连接的回调')
    },
    //接收一次数据段时回调,因为是流式返回,所以这个回调会被调用多次
    onmessage(msg) {
      handleMessage(msg)
    },
    //正常结束的回调
    onclose() {
      controller.abort() //关闭连接
    },
    //连接出现异常回调
    onerror(err) {
      ElMessage({ message: err, type: 'error' })
      // 必须抛出错误才会停止
      throw err
    },
  })
}

// 停止生成数据
export const stopRequest = () => {
  if (controller) {
    controller.abort()
    controller = new AbortController()
  }
}

注意事项:如果想前端主动停止流式输出的话,可以通过AbortController配置(不过在AI对话场景中不建议这样使用,因为这个方案为前端主动停止接收数据,而后端实际上是无感知的,在查看历史消息时会造成数据不一致!!!建议还是让后端创造一个停止接口

3、 关键排坑:Nginx 代理配置

在实际场景中我们发现前端代码没问题,但输出却是"一大块一大块"地蹦,没有打字机效果。这通常是因为 Nginx 开启了响应缓冲。

所以在配置 Nginx 代理时,必须显式关闭 proxy_buffering:

Nginx 复制代码
location ^~/api/v1/chat/ {
      proxy_pass http://backend_server;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_read_timeout   3600s; // 防止长连接超时断开
      
      # 核心配置:关闭代理缓冲,实现流式即时传输
      proxy_buffering off; 
      # 开启分块传输编码
      chunked_transfer_encoding on; 
}

四、SSE和WebSocket区别

关于WebSocket的概念与具体使用事项可以看这个文章:深度拆解 WebSocket

特性 SSE WebSocket
通信方向 单向(Server → Client) 全双工(双向互发)
协议基础 标准 HTTP/1.1 独立的 WS 协议(需握手升级)
数据格式 纯文本/JSON 二进制或文本
复杂度 轻量级,接入成本低 较重,逻辑复杂

五、 深度思考:为何 AI 对话多选择 SSE 而非 WebSocket?

  1. 功能契合度:AI 场景中前端只发一次请求(问题),后端持续输出(回答)。双向通信的 WebSocket 属于"功能过剩"。
  2. 兼容性与成本:SSE 基于标准 HTTP 协议,无需协议升级,天然支持流式输出,对 Nginx、网关的穿透性更好。

相关推荐
架构技术专栏5 小时前
Agent 工具调用:从工具声明到执行反馈
前端·人工智能·后端
别动我齐刘海6 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
志尊宝7 小时前
Vue3 零基础每日笔记(071):暗黑模式与主题切换——CSS 变量 + Element Plus 暗黑方案
前端·css·笔记·vue·html·前端开发·软件开发
考研保研资料分享7 小时前
新闻传播保研经验:中传预推免面试、广告学代表作与实习准备(2025)
面试·职场和发展
刘发财7 小时前
前端如何生成10000页PDF文件?
前端·javascript·开源
A接拉起0078 小时前
2026 年 JavaScript 开发者需要了解的知识
前端·javascript·后端
_馒头8 小时前
pixiJs游戏开发实践 - 跳棋小游戏
javascript·vue.js
用户11481867894848 小时前
面试官:大屏项目里十几个模块同时渲染上万条数据,怎么做到不卡?
前端·性能优化
前端市界8 小时前
Pro React Admin: 统一工程底座,承载多种应用...
前端·github
葡萄城技术团队8 小时前
dsh-spreadjs-editor 当前支持什么:安装前需要了解的几件事
前端