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、网关的穿透性更好。

相关推荐
园小异3 小时前
2026年技术面试完全指南:从算法到系统设计的实战突破
算法·面试·职场和发展
m0_719084114 小时前
React笔记张天禹
前端·笔记·react.js
Ziky学习记录4 小时前
从零到实战:React Router 学习与总结
前端·学习·react.js
wuhen_n4 小时前
JavaScript链表与双向链表实现:理解数组与链表的差异
前端·javascript
wuhen_n4 小时前
JavaScript数据结构深度解析:栈、队列与树的实现与应用
前端·javascript
我是一只puppy4 小时前
使用AI进行代码审查
javascript·人工智能·git·安全·源代码管理
颜酱4 小时前
从二叉树到衍生结构:5种高频树结构原理+解析
javascript·后端·算法
狗哥哥5 小时前
微前端路由设计方案 & 子应用管理保活
前端·架构
前端大卫5 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端