前端实现chatGpt流式输出 - SSE

前端实现chatGpt流式输出 - SSE

一、chatGpt流式输出技术分析

在这里插入图片描述
在使用ChatGPT时,模型的回复内容是连续输出,而不是整段话直接出现,因为模型需要不断预测接下来要回复什么内容,如果等整段回复生成之后再输出到网页,用户体验就会很差,后面才了解到使用SSE技术可以实现。

相关知识小tips

  • 长轮询:客户端向服务器发送Ajax请求,服务器接到请求后保持连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
    在这里插入图片描述
  • 长连接:保持长时间的连接,服务器发送数据后,连接不关闭,下次有新数据时仍然用此连接发送
    在这里插入图片描述

二、SSE介绍

Server-Sent Events (SSE)是一种用于实现服务器向客户端实时推送数据的Web技术,它允许服务器向客户端发送数据和信息。与 WebSocket 不同,SSE 是一种单向通信方式,只有服务器可以向客户端推送消息。与传统的轮询和长轮询相比,SSE提供了更高效和实时的数据推送机制。

SSE基于HTTP协议,允许服务器将数据以事件流(Event Stream)的形式发送给客户端。客户端通过建立持久的HTTP连接,并监听事件流,可以实时接收服务器推送的数据。

SSE的主要特点包括:

  • 简单易用:SSE使用基于文本的数据格式,如纯文本、JSON等,使得数据的发送和解析都相对简单。
  • 单向通信:SSE支持服务器向客户端的单向通信,服务器可以主动推送数据给客户端,而客户端只能接收数据。
  • 实时性:SSE建立长时间的连接,使得服务器可以实时地将数据推送给客户端,而无需客户端频繁地发起请求。
与WebSocket的比较

WebSocket是另一种用于实现实时双向通信的Web技术,它与SSE在某些方面有所不同。下面是SSE和WebSocket之间的比较:

  • 数据推送方向:SSE是服务器向客户端的单向通信,服务器可以主动推送数据给客户端。而WebSocket是双向通信,允许服务器和客户端之间进行实时的双向数据交换。
  • 连接建立:SSE使用基于HTTP的长连接,通过普通的HTTP请求和响应来建立连接,从而实现数据的实时推送。WebSocket使用自定义的协议,通过建立WebSocket连接来实现双向通信。
  • 兼容性:由于SSE基于HTTP协议,它可以在大多数现代浏览器中使用,并且不需要额外的协议升级。WebSocket在绝大多数现代浏览器中也得到了支持,但在某些特殊的网络环境下可能会遇到问题。
  • 适用场景:SSE适用于更新频繁、低延迟并且服务器向客户端实时推送数据的场景,如股票价格更新、新闻实时推送等。WebSocket适用于需要实时双向通信的场景,如聊天应用、多人协同编辑等。
    根据具体的业务需求和场景,选择SSE或WebSocket取决于实际需求。如果只需要服务器向客户端单向推送数据,并且希望保持简单易用和兼容性好,那么SSE是一个不错的选择。如果需要实现双向通信,或者需要更高级的功能和控制,那么WebSocket可能更适合。

三、客户端API

3.1 EventSource对象

SSE的客户端API部署在EventSource对象上,可以通过一下代码检测浏览器是否支持SSE。

if ('EventSource' in window) {// ...
}

用 SSE 时,浏览器首先生成一个EventSource实例,向服务器发起连接。

var source = new EventSource(url);

EventSource实例有一个readyState属性,表明连接的当前状态。该属性只读,可以取以下值。

0:相当于常量EventSource.CONNECTING,表示连接还未建立,或者断线正在重连。
1:相当于常量EventSource.OPEN,表示连接已经建立,可以接受数据。
2:相当于常量EventSource.CLOSED,表示连接已断,且不会重连。
3.2 传参
3.2.1 原生EventSource(get)
  const eventSource = new EventSource('/api/test');// 正常的EventSource,我们只关心以下三个事件/** open:订阅成功(和后端连接成功)*/eventSource.addEventListener("open", function(e) {console.log('open successfully')})/** message:后端返回信息,格式可以和后端协商*/eventSource.addEventListener("message", function(e) {console.log(e.data)})/** error:错误(可能是断开,可能是后端返回的信息)*/eventSource.addEventListener("error", function(err) {console.log(err)// 类似的返回信息验证,这里是实例err && err.status === 401 && console.log('not authorized')})// 需要关闭了eventSource.close()

Tip: 我们值得注意的语法:new EventSource(url, configuration)

1、参数
(1) url:一个USVString ,它代表远程资源的位置
(2) configuration 可选:为配置新连接提供选项。可选项是:withCredentials,默认为 false,指示 CORS 是否应包含凭据( credentials )。
2、返回值
一个新建的 EventSource 对象,如果指定了configuration, 则按其配置;否则,配置为合适的基本默认值。
3.2.2 EventSource - post
npm i --save @rangermauve/fetch-event-sourceimport { fetchEventSource } from '@microsoft/fetch-event-source';

代码示例

	// 	Axios 支持以 fetch API 方式—— AbortController 取消请求//  AbortController 接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求。const controller = new AbortController();fetchEventSource(url, {method: 'post',headers: {'Content-Type': 'application/json',},body: JSON.stringify(params),signal: controller.signal,openWhenHidden: true,async onopen(response) {...},onerror() {// 取消请求controller.abort()console.log('error')},onmessage(evt) {console.log('open successfully')},})

Tips:AbortController
Controller 和 Signal
下面实例化了一个AbortController,它的signal属性就是一个AbortSignal

const controller = new AbortController();
const { signal } = controller;
  • controller 可通过controller.abort()去终止它对应的signal
  • signal本身是不能被直接终止的。可以将它传递给一些函数调用如 fetch 或者直接监听signal的状态变化(可以通过signal.aborted查看signal的状态或者监听它的abort事件)
3.3 基本用法

连接一旦建立,就会触发open事件,可以在onopen属性定义回调函数。

source.onopen = function (event) {// ...
};// 另一种写法
source.addEventListener('open', function (event) {// ...
}, false);

客户端收到服务器发来的数据,就会触发message事件,可以在onmessage属性的回调函数。

source.onmessage = function (event) {var data = event.data;// handle message
};// 另一种写法
source.addEventListener('message', function (event) {var data = event.data;// handle message
}, false);

上面代码中,事件对象的data属性就是服务器端传回的数据(文本格式)。

如果发生通信错误(比如连接中断),就会触发error事件,可以在onerror属性定义回调函数。

source.onerror = function (event) {// handle error event
};// 另一种写法
source.addEventListener('error', function (event) {// handle error event
}, false);

close方法用于关闭 SSE 连接。

source.close();
3.4 自定义事件

默认情况下,服务器发来的数据,总是触发浏览器EventSource实例的message事件。但是我们也可以自定义 SSE 事件,这种情况下,发送回来的数据不会触发message事件。

source.addEventListener('foo', function (event) {var data = event.data;// handle message
}, false);

上面代码中,浏览器对 SSE 的foo事件进行监听。如何实现服务器发送foo事件,请看下文。

四、服务器实现

4.1 数据格式

服务器向浏览器发送的 SSE 数据,必须是 UTF-8 编码的文本,具有如下的 HTTP 头信息。

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

上面三行之中,第一行的Content-Type必须指定 MIME 类型为event-steam

每一次发送的信息,由若干个message组成,每个message之间用\n\n分隔。每个message内部由若干行组成,每一行都是如下格式。

[field]: value\n

上面的field可以取四个值。

data
event
id
retry

此外,还可以有冒号开头的行,表示注释。通常,服务器每隔一段时间就会向浏览器发送一个注释,保持连接不中断。

: This is a comment

下面是一个例子。

: this is a test stream\n\ndata: some text\n\ndata: another message\n
data: with two lines \n\n
4.2 data字段

数据内容用data字段表示

data:  message\n\n

如果数据很长,可以分成多行,最后一行用\n\n结尾,前面行都用\n结尾。

data: begin message\n
data: continue message\n\n

下面是一个发送 JSON 数据的例子。

data: {\n
data: "foo": "bar",\n
data: "baz", 555\n
data: }\n\n
4.3 id 字段

数据标识符用id字段表示,相当于每一条数据的编号。

id: msg1\n
data: message\n\n

浏览器用lastEventId属性读取这个值。一旦连接断线,浏览器会发送一个 HTTP 头,里面包含一个特殊的Last-Event-ID头信息,将这个值发送回来,用来帮助服务器端重建连接。因此,这个头信息可以被视为一种同步机制。

4.4 event 字段

event字段表示自定义的事件类型,默认是message事件。浏览器可以用addEventListener()监听该事件。

event: foo\n
data: a foo event\n\ndata: an unnamed event\n\nevent: bar\n
data: a bar event\n\n

上面的代码创造了三条信息。第一条的名字是foo,触发浏览器的foo事件;第二条未取名,表示默认类型,触发浏览器的message事件;第三条是bar,触发浏览器的bar事件。

下面是另一个例子。

event: userconnect
data: {"username": "bobby", "time": "02:33:48"}event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}event: userdisconnect
data: {"username": "bobby", "time": "02:34:23"}event: usermessage
data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}

4.5 retry 字段
服务器可以用retry字段,指定浏览器重新发起连接的时间间隔。

retry: 10000\n

两种情况会导致浏览器重新发起连接:一种是时间间隔到期,二是由于网络错误等原因,导致连接出错。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/97380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【visual studio 小技巧】项目属性->生成->事件

需求 我们有时会用到一些dll,需要把这些dll和我们生成的exe放到一起,一般我们是手动自己copy, 这样发布的时候,有时会忘记拷贝这个dll,导致程序运行出错。学会这个小技巧,就能实现自动copy,非…

jira+confluence安装

准备如下所有包: atlassian-agent.jar jdk-8u241-linux-x64.tar.gz atlassian-confluence-8.0.0-x64.bin atlassian-jira-software-9.4.0-x64.bin mysql-8.0.31-1.el8.x86_64.rpm-bundle.tar mysql-connector-java-8.0.28.jar confluence-8.2.1破解 1.安装j…

冲刺第十五届蓝桥杯P0003倍数问题

文章目录 原题连接解析代码 原题连接 倍数问题 解析 需要找出三个数字,三个数字之和是k的倍数,并且这个数字需要最大,很容易想到的就是将数组进行倒叙排序,然后三层for循环解决问题,但是这样会导致**时间复杂度很高…

sheng的学习笔记-【中文】【吴恩达课后测验】Course 2 - 改善深层神经网络 - 第二周测验

课程2_第2周_测验题 目录:目录 第一题 1.当输入从第8个mini-batch的第7个的例子的时候,你会用哪种符号表示第3层的激活? A. 【  】 a [ 3 ] { 8 } ( 7 ) a^{[3]\{8\}(7)} a[3]{8}(7) B. 【  】 a [ 8 ] { 7 } ( 3 ) a^{[8]\{7\}(3)} a…

hive 常用函数

1.分位数 percentile_approx(DOUBLE col, p [, B]) Returns an approximate pth percentile of a numeric column (including floating point types) in the group 含义: 在col列中返回p%的分位数 select percentile_approx( arr_id , 0.5 )from (selectarr_idfrom(selecta…

力扣-404.左叶子之和

Idea attention:先看清楚题目,题目说的是左叶子结点,不是左结点【泣不成声】 遇到像这种二叉树类型的题目呢,我们一般还是选择dfs,然后类似于前序遍历的方式加上判断条件即可 AC Code class Solution { public:void d…

【JavaEE】_HTTP请求与HTTP响应

目录 1. HTTP协议 2. HTTP请求 2.1 HTTP请求首行 2.2 URL 2.3 HTTP方法 2.3.1 GET请求 2.3.2 POST请求 2.3.3 GET与POST的区别 2.3.4 其他方法 2.4 请求报头header 2.4.1 Host: 2.4.2 Content-Length与Content-Type: 2.4.3 User-Agent&…

王道考研计算机组成原理——计算机硬件的基础知识

计算机组成原理的基本概念 计算机硬件的针脚都是用来传递信息,传递数据用的: 服务程序包含一些调试程序: 计算机硬件的基本组成 控制器通过电信号来协调其他部件的工作,同时负责解析存储器里存放的程序指令,然后指挥…

数据结构--》数组和广义表:从基础到应用的全面剖析

数据结构为我们提供了组织和处理数据的基本工具。而在这个广袤的数据结构领域中,数组和广义表是两个不可或缺的重要概念。它们作为线性结构的代表,在算法与应用中扮演着重要的角色。 无论你是初学者还是进阶者,本文将为你提供简单易懂、实用可…

十二、Django之模板的继承+用户列表

模板的继承 新建layout.html&#xff1a; {% load static %} <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><link rel"stylesheet" href"{% static plugins…

计算机毕设 大数据B站数据分析与可视化 - python 数据分析 大数据

文章目录 0 前言1 课题背景2 实现效果3 数据获取4 数据可视化5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做…

阿里云轻量应用服务器有月流量限制吗?

阿里云轻量应用服务器限制流量吗&#xff1f;部分限制&#xff0c;2核2G3M和2核4G4M这两款轻量应用服务器不限制月流量&#xff0c;其他的轻量服务器套餐有月流量限制。 腾讯云轻量应用服务器价格便宜&#xff0c;活动页面&#xff1a;aliyunbaike.com/go/tencent 细心的同学看…

第四课 递归、分治

文章目录 第四课 递归、分治lc78.子集--中等题目描述代码展示 lc77.组合--中等题目描述代码展示 lc46.全排列--中等题目描述代码展示 lc47.全排列II--中等题目描述代码展示 lc226.翻转二叉树--简单题目描述代码展示 lc98.验证二叉搜索树--中等题目描述代码展示 lc104.二叉树的最…

八大排序详解(默认升序)

一、直接插入排序 直接插入排序&#xff1a;直接插入排序就是像打扑克牌一样&#xff0c;每张牌依次与前面的牌比较&#xff0c;遇到比自己大的就将大的牌挪到后面&#xff0c;遇到比自己小的就把自己放在它后面(如果自己最小就放在第一位)&#xff0c;所有牌排一遍后就完成了排…

GitHub工具之云资产管理

文章目录 0x01 介绍0x02 软件架构0x03 下载地址0x04 更新记录0x05 实现功能0x06 使用截图1、云存储工具-资产列表2、云存储工具-阿里云3、云存储工具-七牛云4、云存储工具-腾讯云5、云存储工具-亚马逊6、云存储工具-京东云7、云存储工具-金山云8、云存储工具-其他9、云存储工具…

【Kotlin精简】第1章 基础类型

1 Kotlin基础类型 Kotlin中&#xff0c;我们可以调用任何变量的成员函数和属性&#xff0c;从这个角度来说&#xff0c;一切皆对象。某些类型可以有特殊的内部表现。例如&#xff1a;数字、字符和布尔型在运行时可以表现为基础类型&#xff08;primitivetypes&#xff09;。 …

Stable diffusion的架构解读(本博客还是以unet架构为主)

博客只是简单的记录一下自己学的&#xff0c;基于自己的一些情况&#xff0c;所以简单了一些只是将来忘记&#xff0c;用来回顾用。 论文的大体框架 unet结构位于 unet会接受prompt特征、latent特征、和t时间步特征&#xff0c;最后生成新一轮的特征 可以参考知乎大佬htt…

Android之App跳转其他软件

文章目录 前言一、效果图二、实现步骤1.弹框xml(自己替换图标)2.弹框utils3.两个弹框动画4.封装方便调用5.调用6.长按事件方法7.跳转步骤8.复制utils 总结 前言 最近遇到一个需求&#xff0c;就是App内大面积需要长按复制并跳转指定App&#xff0c;没办法&#xff0c;只能埋头…

大语言模型之十六-基于LongLoRA的长文本上下文微调Llama-2

增加LLM上下文长度可以提升大语言模型在一些任务上的表现&#xff0c;这包括多轮长对话、长文本摘要、视觉-语言Transformer模型的高分辨4k模型的理解力以及代码生成、图像以及音频生成等。 对长上下文场景&#xff0c;在解码阶段&#xff0c;缓存先前token的Key和Value&#…

SpringCloud Alibaba - Sentinel 高级玩法,修改 Sentinel-dashboard 源码,实现 push 模式

目录 一、规则持久化 1.1、什么是规则持久化 1.1.1、使用背景 1.1.2、规则管理的三种模式 a&#xff09;原始模式 b&#xff09;pull 模式 c&#xff09;push 模式 1.2、实现 push 模式 1.2.1、修改 order-service 服务&#xff0c;使其监听 Nacos 配置中心 1.2.2、修…