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);
}
相关推荐
whabc1007 分钟前
和鲸社区深度学习基础训练营2025年关卡2(2)sklearn中的MLPClassifier
人工智能·深度学习·numpy
往日情怀酿做酒 V176392963830 分钟前
pytorch的介绍以及张量的创建
人工智能·pytorch·python
北辰alk42 分钟前
如何实现AI多轮对话功能及解决对话记忆持久化问题
人工智能
智驱力人工智能42 分钟前
极端高温下的智慧出行:危险检测与救援
人工智能·算法·安全·行为识别·智能巡航·高温预警·高温监测
Leo.yuan1 小时前
数据分析师如何构建自己的底层逻辑?
大数据·数据仓库·人工智能·数据挖掘·数据分析
笑稀了的野生俊1 小时前
ImportError: /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.32‘ not found
linux·人工智能·ubuntu·大模型·glibc·flash-attn
吕永强1 小时前
意识边界的算法战争—脑机接口技术重构人类认知的颠覆性挑战
人工智能·科普
二二孚日1 小时前
自用华为ICT云赛道AI第三章知识点-昇腾芯片硬件架构,昇腾芯片软件架构
人工智能·华为
蹦蹦跳跳真可爱5892 小时前
Python----OpenCV(几何变换--图像平移、图像旋转、放射变换、图像缩放、透视变换)
开发语言·人工智能·python·opencv·计算机视觉
蹦蹦跳跳真可爱5892 小时前
Python----循环神经网络(Transformer ----Layer-Normalization(层归一化))
人工智能·python·rnn·transformer