ChatGPT、通义千问这些市面上的聊天式人工智能用的什么技术传输数据?一起来认识一下SSE

抛出问题

通义千问2.5发布后,我发现准确率有了很大的提升,但是处理任务的时长并没有明显的提升,不知道用过通义千问的小伙伴们发现没有,通义千问响应的内容是持续性输出的,而不是一次全部返回。

发现问题

在监听网络请求之后,我发现一个有意思的请求dialog/conversation,这个接口在返回200状态码之后持续输出了一分多钟。 请求头

那么这个EventStream是个啥东西呢?

SSE 流式传输

这个时候,我们就不得不说SSE了,SSE 全称为 Server-sent events , 是一种基于 HTTP 协议的通信技术,允许服务端主动推送数据到客户端。客户端一旦和服务端建立连接之后,服务端就有源源不断的像客户端发送数据,无需客户端再次发送新的请求。

主要特点:

  • 单向通信:SSE主要用于服务器向客户端推送数据,不支持客户端向服务器发送消息,WebSocket在连接建立时借用了HTTP的机制,但它本身是一个独立的协议,运行在TCP之上,并且在握手之后的操作与HTTP完全不同,提供了低延迟、双向通信的能力,适用于实时数据传输的场景。这与WebSocket的双向通信有所不同。
  • 简单易用:相对于WebSocket等复杂协议,SSE在实现上更为简单,易于集成到现- 有的Web应用程序中。
  • 兼容性好:作为HTML5标准的一部分,现代浏览器大多支持SSE,无需额外插件。 轻量级:SSE建立在HTTP协议之上,使用简单的文本格式进行数据传输,对网络带宽要求较低。 自动重连:当连接意外中断时,大多数浏览器会自动尝试重新建立与服务器的连接。 事件驱动:数据以事件的形式发送,客户端可以通过JavaScript的EventSource API订阅这些事件并作出响应。

工作原理

  • 客户端通过发送一个HTTP GET请求到一个支持SSE的URL端点。
  • 服务器响应这个请求,并保持这个连接打开状态,而不是像常规HTTP请求那样完成响应后关闭连接。
  • 当服务器有新的数据需要推送时,它通过这个已建立的连接发送一个事件,该事件包含一个类型(event字段)、数据(data字段)以及其他可选字段如id和retry等。
  • 客户端接收到事件后,可以通过EventSource对象的事件处理器(如onmessage, onopen, onerror)来处理这些数据。

应用场景

我们说了,SSE和websocket最大的区别是基于http的单向通信,那就说在维护,传输,宽带占用方面有明显的优势,所以SSE成为了很多实时Web功能实现的首选技术之一,尤其是在那些对实时性要求不是极高,且主要由服务器主导信息流动的场景下。

  • 数据监控,比如股票价格,日志分析,平台监控等
  • 内容推送 比如新闻,赛事播报 实时天气等

实时监控cpu的负载

接下来我们实现一个小应用,实时监控我的mac的cpu负载

  • 前端代码jsx
javascript 复制代码
import React from 'react';

class App extends React.Component {
    //创建sse客户端
    eventSource = null;
    state = {
        buttonText: "暂停", status: true,//是否继续接收数据
        data: []
    }
    componentDidMount() {
        this.handleData(true)
    }

    handleData = (status) => {
        console.log(status);
        const buttonText = status ? "暂停" : "继续"
        this.setState({
            status: status, buttonText: buttonText
        })

        if (status === false) {
            this.eventSource.close()
        }

        if (status === true) {
            this.eventSource = new EventSource('/api/sse/data')
            this.eventSource.onmessage = (event) => {
                const sseData = JSON.parse(event.data)
                //设置时间
                sseData.time = new Date().toLocaleString()
                const historyData = this.state.data
                let newData = [sseData]
                if (historyData.length > 0) {
                    newData = [...historyData, sseData]
                }
                this.setState({
                    data: newData
                })
            };
        }
    }

    render() {
        const {data, status, buttonText} = this.state
        return (<div>
            <button onClick={() => this.handleData(!status)}>{buttonText}</button>
            <table>
                <thead>
                <tr>
                    <th>时间</th>
                    <th>一分钟内cpu负载</th>
                    <th>五分钟内cpu负载</th>
                    <th>十五分钟内cpu负载</th>
                </tr>
                </thead>
               <tbody>
               {data ? data.map((item, index) =>
                   <tr key={index}>
                       <th>{item.time}</th>
                       <th>{item.load1}</th>
                       <th>{item.load5}</th>
                       <th>{item.load15}</th>
                   </tr>

               ) : ""}
               </tbody>

            </table>
        </div>)
    }
}

export default App;
  • 用go起了一个后端服务,代码如下
javascript 复制代码
func SseDataHandler(c *gin.Context) {
	// 设置头部信息,表明这是一个事件流
	c.Writer.Header().Set("Content-Type", "text/event-stream")
	c.Writer.Header().Set("Cache-Control", "no-cache")
	c.Writer.Header().Set("Connection", "keep-alive")

	// 创建一个通道,并不断发送时间信息
	ticker := time.NewTicker(30 * time.Second)
	defer ticker.Stop()

	for range ticker.C {
		//获取cpu负载信息
		stat, err := load.Avg()
		// 打印或进一步处理输出
		data := fmt.Sprintf("data: %s\n\n", stat)
		// 写数据到客户端
		_, err = c.Writer.WriteString(data)
		c.Writer.Flush() // 刷新数据,确保发送到客户端

		if err != nil {
			return // 如果写入有错误,结束循环
		}

		select {
		case <-c.Request.Context().Done(): // 检查客户端是否还连接着
			return
		default:
			continue
		}
	}
}
  • 下面是页面输出情况
相关推荐
小远yyds13 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端1 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱1 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
许野平1 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓3 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4113 小时前
无网络安装ionic和运行
前端·npm
理想不理想v3 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云3 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js