GPTBots使用fetch-event-source实现SSE POST传参

摘要:

SSE(Server-Sent Events)技术因其基于HTTP、原生支持以及实现简单的特点,非常适合大模型流式输出数据的场景,许多大模型厂商因此提供支持SSE的API。GPTBots也推出了支持SSE的对话API,近期对Web页面进行技术升级,SSE使用标准Event Source API被改造为基于fetch-event-source实现,从而支持POST请求传参。本文将围绕相关技术知识展开,进行对比分析并解析其原理。

一、什么是SSE

SSE(Server-Sent Events)是一种基于HTTP协议的服务器推送技术,允许服务器向客户端发送实时更新。它是HTML5标准的一部分,旨在提供一种轻量级的方式,使浏览器能够接收来自服务器的事件流。SSE的核心是EventSource接口,它为客户端提供了与服务器建立单向连接的能力,从而实现消息的实时传递。

SSE的工作原理非常简单:客户端通过HTTP请求向服务器发起连接,服务器在保持连接的同时,持续地以文本流的形式发送事件数据。客户端接收到数据后,可以通过JavaScript代码处理这些事件。

SSE的主要特点包括:

  • 单向通信:服务器可以主动向客户端推送数据,但客户端无法通过同一连接向服务器发送数据。

  • 自动重连:当连接意外断开时,浏览器会自动尝试重新连接。

  • 轻量级协议:基于HTTP协议,易于实现和调试。

  • 支持事件类型:支持自定义事件类型,便于对不同类型的数据流进行处理。

SSE非常适合大模型流式返回数据的场景,因此很多模型服务厂商提供了SSE的接口,前端接入后可以实现打字机效果,实时输出和展示大模型的回复内容。

二、SSE和WebSocket技术对比

SSE和WebSocket都是实现实时通信的技术,但它们的设计理念和适用场景存在显著差异。

|-------|------------------|----------------------|
| 特性 | SSE | WebSocket |
| 通信方向 | 单向(服务器 -> 客户端) | 双向(客户端 <-> 服务器) |
| 协议 | 基于HTTP协议 | 独立的WebSocket协议 |
| 连接数 | 每个客户端需要单独的HTTP连接 | 单个WebSocket连接支持双向通信 |
| 浏览器支持 | 原生支持,兼容性较好 | 需要浏览器支持WebSocket API |
| 重连机制 | 内置自动重连 | 需要手动实现重连逻辑 |
| 数据格式 | 纯文本(UTF-8编码) | 支持文本和二进制数据 |
| 实现复杂度 | 简单,基于HTTP | 较复杂,需要独立的协议实现 |
| 适用场景 | 实时新闻、日志流、事件通知等 | 聊天应用、多人游戏、实时协作等复杂场景 |

选择SSE还是WebSocket,取决于具体的业务需求。如果只需要服务器向客户端推送数据,且对通信性能要求不高,SSE是一个简单高效的选择;而如果需要双向通信或对性能要求较高,WebSocket则更为适合。

三、标准的EventSource API

在浏览器中,SSE通过EventSource对象实现。以下是EventSource API的主要特性和用法:

1. 创建EventSource对象

使用EventSource对象时,需要指定服务器的URL:

复制代码
const eventSource = new EventSource('https://example.com/sse');

2. 事件监听

EventSource支持三种事件类型:

  • message:处理默认事件。

  • open:连接成功时触发。

  • error:连接出错或断开时触发。

示例代码:

复制代码
eventSource.onopen = function(event) {
  console.log('Connection opened:', event);
};

eventSource.onmessage = function(event) {
  console.log('Message received:', event.data);
};

eventSource.onerror = function(event) {
  console.error('Error occurred:', event);
};

3. 自定义事件类型

服务器可以发送自定义事件类型,客户端可以通过addEventListener监听:

复制代码
eventSource.addEventListener('custom-event', function(event) {
  console.log('Custom event received:', event.data);
});

4. 关闭连接

可以通过调用close()方法手动关闭连接:

复制代码
eventSource.close();
console.log('Connection closed');

5. 服务器发送数据格式

服务器需要按照以下格式发送数据:

复制代码
data: Hello, World!
event: custom-event
id: 12345
retry: 3000
  • data:事件数据。

  • event:事件类型(可选)。

  • id:事件ID,用于断线重连时恢复状态(可选)。

  • retry:指定重连间隔时间(以毫秒为单位,可选)。

四、@microsoft/fetch-event-source 介绍

@microsoft/fetch-event-source是微软开发的一个轻量级库,用于在Node.js和浏览器环境中实现SSE(Server-Sent Events)。与原生的EventSource相比,它提供了更多的灵活性和功能,例如支持自定义HTTP方法(如POST)、请求头、认证、请求体等。

npm仓库:@microsoft/fetch-event-source - npm

1. 安装

可以通过npm或yarn安装:

复制代码
npm install @microsoft/fetch-event-source

2. 使用示例

以下是一个简单的使用示例:

复制代码
import { fetchEventSource } from '@microsoft/fetch-event-source';

fetchEventSource('https://example.com/sse', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer YOUR_TOKEN',
  },
  body: JSON.stringify({ key: 'value' }),
  onopen(response) {
    if (response.ok && response.headers.get('content-type') === 'text/event-stream') {
      console.log('Connection established');
    } else {
      console.error('Connection failed');
    }
  },
  onmessage(event) {
    console.log('Message received:', event.data);
  },
  onclose() {
    console.log('Connection closed by server');
  },
  onerror(err) {
    console.error('Error occurred:', err);
  },
});

3. 特性

  • 支持自定义HTTP方法(如POST)。

  • 支持请求头和请求体。

  • 提供事件钩子(如onopenonmessageoncloseonerror)。

  • 支持断线重连。

五、@microsoft/fetch-event-source 原理解析

@microsoft/fetch-event-source的实现基于fetch API,通过流式处理实现了SSE的功能。以下是其核心实现原理的解析:

1. 核心逻辑

该库的核心逻辑是使用fetch API发起HTTP请求,并通过ReadableStream解析服务器返回的事件流。

源码片段(简化版):

复制代码
async function fetchEventSource(url, options) {
  const response = await fetch(url, options);
  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  let buffer = '';
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\n');
    buffer = lines.pop(); // 保留未完整的行

    for (const line of lines) {
      if (line.startsWith('data:')) {
        const data = line.slice(5).trim();
        options.onmessage({ data });
      }
    }
  }

  options.onclose();
}

2. POST传参的实现

与原生EventSource不同,该库允许通过POST方法发送参数。实现方式是将请求体传递给fetch API:

复制代码
fetch(url, {
  method: 'POST',
  headers: options.headers,
  body: options.body,
});

3. 自动重连

当连接断开时,库会自动尝试重新连接:

复制代码
setTimeout(() => {
  fetchEventSource(url, options);
}, retryInterval);

4. 错误处理

通过try-catch捕获错误,并调用onerror回调:

复制代码
try {
  // 读取流数据
} catch (err) {
  options.onerror(err);
}
相关推荐
奔跑中的小象2 小时前
统信UOS + 天数AI卡部署SGLang服务手册
人工智能·uos·sglang·天数智芯
DevSecOps选型指南2 小时前
中国版Mythos,为何是悬镜安全灵脉CodeAI?
人工智能·安全
Shockang2 小时前
LangGraph 状态机实战
人工智能
刹那芳华19922 小时前
循环神经网络的从零开始实现(RNN)
人工智能·rnn·深度学习
阿童木写作2 小时前
跨境图片翻译工具多合一,批量图片视频字幕翻译加智能抠图
人工智能·python·音视频·语音识别
科技之门2 小时前
AI3D从建模到贴图、绑骨和动画的完整流程怎么做?V2Fun完整工作流指南
人工智能·3d·贴图
宇宙第一小趴菜2 小时前
二、机器学习的应用领域和发展史
人工智能·机器学习
前端开发江鸟2 小时前
我写过 MCP Server,却一直以为 MCP 只有 Tool
人工智能
天国梦3 小时前
自习室智能化升级避坑指南:天学网AI智习室方案实测与选型建议
大数据·人工智能
阿里云云原生3 小时前
可用性从 99.9% 跃升至 99.995%:畅捷通如何用 AI 重塑运维底座?
运维·网络·人工智能