前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >了解ChatGPT流式响应背后的技术,优化数据流处理效率!

了解ChatGPT流式响应背后的技术,优化数据流处理效率!

原创
作者头像
brzhang
发布2023-05-06 10:40:08
9.1K3
发布2023-05-06 10:40:08
举报
文章被收录于专栏:玩转全栈玩转全栈

背景

我们知道 ,ChatGPT API是一个OpenAI 的聊天机器人接口,它可以根据用户的输入生成智能的回复。为了提高聊天的流畅性和响应速度,ChatGPT API采用了SSE作为服务端推送技术。SSE是一种HTML5技术,它允许服务器向客户端发送事件,从而实现服务器端推送。相对于WebSockets或长轮询技术,SSE提供了更简单的方式来实现服务器端推送,并且支持更广泛的客户端和服务器端。

SSE在ChatGPT API中的应用如下:

  • 客户端通过一个HTTP GET请求建立与服务器的连接,并指定接收text/event-stream类型的数据。
  • 服务器在收到请求后,不立即返回响应,而是保持连接打开,并根据用户的输入生成回复。
  • 服务器在生成回复后,将回复作为一个事件发送给客户端,并保持连接打开,等待下一个输入。
  • 客户端在收到事件后,解析事件中的数据,并显示在聊天界面上。
  • 客户端和服务器之间可以通过同一个连接持续交换数据,直到客户端关闭连接或者服务器出现异常。

通过SSE技术,ChatGPT API可以实现流式响应,即服务器不需要等待客户端的请求,就可以主动发送数据给客户端。这样可以减少网络延迟和资源消耗,提高聊天的效率和质量。

在Web开发中,有时我们需要从服务器端实时地向浏览器端发送数据,以提高用户体验和交互效果。例如,聊天应用、股票行情、新闻更新等场景都需要服务器端主动地推送数据给浏览器端。那么,如何实现这样的功能呢?没错,依然是SSE。

SSE相比于其他技术方案,SSE有以下几个优势:

  • SSE使用更简单,不需要添加任何新组件,只需使用现有的后端语言和框架即可。
  • SSE完全复用现有的HTTP协议,因此可以直接运行于现有的代理服务器和认证技术。
  • SSE在浏览器端提供了原生的EventSource对象,可以方便地监听和处理服务器发送的事件。
  • SSE支持断线重连和消息追踪的功能,可以保证数据的完整性和一致性。

以下是一个使用EventSource实现实时推送消息的例子:

HTML代码:

代码语言:javascript
复制
html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>EventSource Example</title>
</head>
<body>
  <div id="messages"></div>
  <script>
    var source = new EventSource('/message-stream');

    source.onmessage = function(event) {
      var message = JSON.parse(event.data);
      var div = document.createElement('div');
      div.innerHTML = message.user + ': ' + message.text;
      document.getElementById('messages').appendChild(div);
    };
  </script>
</body>
</html>

Node.js服务器端代码:

代码语言:javascript
复制
var http = require('http');

http.createServer(function(request, response) {
  response.writeHead(200, {
    'Content-Type': 'text/event-stream',// 这个表示服务端流式响应
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });

  setInterval(function() {
    var message = {
      user: 'John',
      text: 'Hello, world!'
    };
    response.write('data: ' + JSON.stringify(message) + '\n\n');
  }, 1000);
}).listen(3000);

当然,SSE也有一些局限性,比如:

  • SSE只支持从服务器到客户端的单向数据传输,如果需要双向通信,还需要使用Ajax或WebSocket等技术。
  • SSE只支持文本格式的数据,如果需要传输二进制数据,还需要使用Base64等编码方式。
  • SSE在浏览器方面支持不够广泛,IE和Edge几乎不支持SSE。

因此,在选择使用SSE技术之前,需要根据具体的应用场景和需求进行权衡。如果只需要从服务器向客户端发送更新频繁、低延迟的文本数据,并且不考虑IE和Edge浏览器的兼容性问题,那么SSE是一个很好的选择。

下面我们来具体介绍一下SSE的技术细节和实现方法。

SSE的通信协议

要使用SSE技术,首先需要了解它的通信协议。SSE通信协议很简单,本质上就是一个客户端发起的HTTP GET请求,服务器在接收到该请求后,返回200 OK状态,并附带以下响应头:

代码语言:javascript
复制
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

这些响应头的含义分别是:

  • Content-Type: text/event-stream 表示响应的内容类型是SSE格式的文本流。
  • Cache-Control: no-cache 表示响应的内容不应该被缓存,以保证实时性。
  • Connection: keep-alive 表示响应的连接应该保持打开,以便服务器端持续发送数据。

在返回响应头之后,服务器端就可以开始向客户端发送数据了。SSE格式的数据是由一系列的事件组成的,每个事件都有以下几个部分:

  • 一个或多个字段,用冒号和空格分隔字段名和字段值,每个字段占一行。
  • 一个空行,表示事件的结束。

SSE支持以下几种字段:

  • data: 表示事件的数据内容,可以有多行,每行都以data: 开头。
  • id: 表示事件的唯一标识符,用于断线重连和消息追踪。
  • event: 表示事件的类型,用于区分不同的事件。
  • retry: 表示断线重连的时间间隔,单位是毫秒。

例如,一个简单的SSE事件可以写成这样:

代码语言:javascript
复制
data: Hello, world!
id: 1
event: message

或者这样:

data: Hello,
data: world!
id: 1
event: message

注意,每个事件必须以一个空行结束,否则客户端无法识别事件的边界。另外,如果一个字段没有值,那么只写字段名即可,例如:

代码语言:javascript
复制
event:

表示一个没有类型的事件。

服务器端可以根据需要发送任意数量和类型的事件,客户端会按照接收到的顺序处理这些事件。如果客户端在接收数据过程中发生了断线或错误,那么它会尝试重新连接服务器,并发送上次接收到的事件id作为Last-Event-ID请求头。服务器端在收到这个请求头后,可以根据id判断是否需要重发之前的事件。

SSE的浏览器实现

要在浏览器端使用SSE技术,只需要使用原生的EventSource对象即可。EventSource对象是一个封装了SSE通信协议的对象,它提供了以下几个属性和方法:

  • url: 表示SSE服务端的URL地址。
  • withCredentials: 表示是否携带cookie等认证信息。
  • readyState: 表示SSE连接的状态,有三种可能的值:0(CONNECTING),1(OPEN),2(CLOSED)。
  • close(): 表示关闭SSE连接。
  • onopen: 表示SSE连接打开时触发的回调函数。
  • onmessage: 表示接收到默认类型(没有event字段)的事件时触发的回调函数。
  • onerror: 表示发生错误时触发的回调函数。

除了这些属性和方法外,EventSource对象还支持addEventListener方法,可以用来监听自定义类型(有event字段)的事件。例如:

代码语言:javascript
复制
var source = new EventSource('http://example.com/sse');
source.onopen = function() {
  console.log('SSE connection opened');
};
source.onmessage = function(e) {
  console.log('Received default event:', e.data);
};
source.onerror = function(e) {
  console.log('SSE error:', e);
};
source.addEventListener('message', function(e) {
  console.log('Received message event:', e.data);
});
source.addEventListener('update', function(e) {
  console.log('Received update event:', e.data);
});

使用EventSource对象非常简单,只需要创建一个实例,并传入SSE服务端的URL地址即可。然后就可以通过onopen、onmessage、onerror等属性或addEventListener方法来监听和处理服务器发送的事件了。

和websock的优缺点对比

WebSockets是一种双向通信协议,它允许客户端和服务器之间建立一个全双工的TCP/IP连接,并在连接上交换二进制或文本数据。WebSockets相比于SSE有以下优缺点:

优点:

  • WebSockets是真正的双向通信协议,客户端和服务器可以随时向对方发送数据,而不需要等待对方的请求或响应。
  • WebSockets支持二进制数据传输,这对于传输图片、音频、视频等大量数据非常有利。
  • WebSockets可以绕过HTTP协议的限制,例如缓存、代理、头部等,实现更高效和灵活的通信。

缺点:

  • WebSockets相对于SSE更复杂,需要额外的组件和库来支持,在一些老旧的浏览器或服务器上可能不兼容。
  • WebSockets需要占用一个独立的端口号,这可能会导致一些防火墙或安全策略的问题。
  • WebSockets由于是二进制协议,调试起来比较困难,需要专门的工具或库来解析数据。

安全性

服务端推送技术涉及到客户端和服务器之间的数据传输,因此需要考虑安全性问题。不同的服务端推送技术有不同的安全性特点:

  • Ajax短轮询和长轮询和基于iframe的流都是基于HTTP协议的,因此可以使用HTTPS协议来加密数据,防止中间人攻击或数据泄露。但是,这些技术都需要频繁地发送请求和响应,这可能会增加服务器的负载和网络的拥塞,也可能会被一些恶意的请求或响应干扰。
  • SSE也是基于HTTP协议的,因此也可以使用HTTPS协议来保证数据的安全性。SSE相比于Ajax轮询技术,只需要建立一次连接,就可以持续地接收服务器的事件,这样可以减少网络开销和服务器压力。但是,SSE只支持单向的通信,即服务器向客户端发送数据,客户端不能向服务器发送数据。这可能会限制一些交互功能的实现。
  • WebSockets是基于TCP/IP协议的,因此可以使用WSS协议来加密数据,防止数据被窃取或篡改。WebSockets支持双向的通信,客户端和服务器可以随时互相发送数据,这样可以实现更丰富和灵活的交互功能。但是,WebSockets需要额外的端口号和组件来支持,在一些环境中可能会遇到兼容性或安全性的问题。

综上所述,服务端推送技术在ChatGPT API中有着重要的应用,它可以提高聊天机器人的响应速度和用户体验。不同的服务端推送技术有各自的优缺点和安全性特点,需要根据具体的场景和需求来选择合适的技术。

koa接口封装为 流式响应demo

代码语言:javascript
复制
/*
*  使用 koa 实现一个 post 的 sse 请求
   请求方式 post
   请求 path /api
   参数straem 控制是否流式响应,stream=true 表示流式响应,否则普通响应
*/

const Koa = require("koa");
const Router = require("koa-router");
const bodyParser = require("koa-bodyparser");

const app = new Koa();
const router = new Router();

// Use bodyParser middleware to parse request body
app.use(bodyParser());

router.post("/api", async (ctx) => {
  const { stream } = ctx.request.body;

  if (stream == true) {
    // Set response headers for SSE
    ctx.response.set({
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
      Connection: "keep-alive",
    });

    // Send SSE header
    ctx.res.statusCode = 200;
    ctx.res.write(":ok\n\n");
    let count = 0;

    // Set up SSE interval for streaming response
    // Generate SSE data
    while (count < 5) {
      const data = {
        message: "Hello, world!",
        timestamp: Date.now(),
      };
      count++;
      await new Promise((resolve) => setTimeout(resolve, 1000));
      // Send SSE event
      ctx.res.write(`data: ${JSON.stringify(data)}\n\n`);
    }
    ctx.res.write(":done\n\n");
    // Handle closing of SSE connection
    ctx.req.on("close", () => {
      ctx.response.end();
    });
  } else {
    // Send non-streaming response
    ctx.body = { message: "Hello, world!" };
  }
});

app.use(router.routes());

app.listen(3000);

使用postman来发起post请求,就可以看到

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
    • SSE在ChatGPT API中的应用如下:
      • SSE相比于其他技术方案,SSE有以下几个优势:
        • 以下是一个使用EventSource实现实时推送消息的例子:
        • SSE的通信协议
        • SSE的浏览器实现
        • 和websock的优缺点对比
        • 安全性
          • koa接口封装为 流式响应demo
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档