在传统的服务端与客户端的数据交互常用方式是:
随着 ChatGPT 诞生,一种新的 SSE 通信方式被广泛推广。
为什么需要这样传输,从使用场景上讲,GPT 需要一定计算和响应时间才能返回用户数据。为避免接口等待时间过长,GPT 将先计算出的数据“推送”给用户,优化体验。
也可以用来将一次获取数据量很大的请求拆分成多个分块依次给客户端去处理,避免消耗太长时间处理完整的数据。
SSE(Server-Sent Events),是一种服务端实时主动向浏览器推送消息的技术。
SSE是 HTML5 中一个与通信相关的 API,主要由两部分组成:
HTTP
协议EventSource
对象。与WebSocket
区别:
Server-Sent Events | WebSocket |
---|---|
基于 HTTP 协议 | 基于 TCP 协议 |
单向,只能服务端单向发送消息 | 双向,可以同时发送和接收消息 |
轻量级,使用简单 | 相对复杂 |
内置断线重连和消息追踪的功能 | 不在协议范围内,需手动实现 |
文本或使用 Base64 编码和 gzip 压缩的二进制消息 | 类型广泛 |
支持自定义事件类型 | 不支持自定义事件类型 |
连接数 HTTP/1.1 6 个,HTTP/2 可协商(默认 100) | 连接数无限制 |
本质是浏览器发起 http 请求,服务器在收到请求后,返回状态与数据,并附带以下
headers:
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
示例代码参考知乎这位大佬的文章:
使用 JavaScript 的 EventSource
API。
使用 JavaScript 的 EventSource API 创建 EventSource 对象监听服务器发送的事件。一旦建立连接,服务器就可以使用 HTTP 响应的 'text/event-stream' 内容类型发送事件消息,浏览器则可以通过监听 EventSource 对象的 onmessage、onopen 和 onerror 事件来处理这些消息。
示例代码:
// 1.创建一个 EventSource 实例
const evtSource = new EventSource("ssedemo.php");
// 如果不同源
const evtSource = new EventSource("//api.example.com/ssedemo.php", {
withCredentials: true,
// withCredentials 属性,表示是否发送凭证(cookie、HTTP认证信息等)到服务端,默认为 false。
});
// 2.监听 message 事件
// 如果服务器发送的消息中没有 event 字段,则这些消息会被视为 message 事件。为了接收这些 message 事件,需要为 message 事件附加一个事件处理程序。
evtSource.onmessage = function (event) {
const newElement = document.createElement("li");
const eventList = document.getElementById("list");
newElement.innerHTML = "message: " + event.data;
eventList.appendChild(newElement);
};
// 3.监听自定义事件
// 如果服务器发送的消息中定义了 event 字段,就会以 event 中给定的名称作为事件接收。例如:
evtSource.addEventListener("ping", (event) => {
const newElement = document.createElement("li");
const eventList = document.getElementById("list");
const time = JSON.parse(event.data).time;
newElement.textContent = `ping at ${time}`;
eventList.appendChild(newElement);
});
// 4.错误处理
evtSource.onerror = (err) => {
console.error("EventSource failed:", err);
};
// 5.关闭事件流
evtSource.close();
发展至今,SSE 已具有广泛的的浏览器兼容性,几乎除 IE 之外的浏览器均已支持。
但是在使用 SSE 时,type 为 eventSource,而 ChatGPT 为 fetch。且受浏览器 EventSource API 限制,在使用 SSE 时不能自定义请求头、只能发出 GET 请求,且在大多数浏览器中,URL 限制 2000个字符,也无法满足 ChatGPT 参数传递需求。此时,可以使用 Fetch API 实现一个替代接口,用于模拟 SSE 实现。简单实现如下: