随着Ai大模型的兴起,越来越多的应用需要集成和利用Ai的能力。
但是,大模型在推理的时候往往需要一些时间,结果也不是一次性返回的,这个时候我们就需要用到服务端向客户端推送消息,也就是SSE.
SSE(Server-Sent Events,服务器发送事件)是一种网络通信技术,允许服务器向客户端推送信息,而不需要客户端显式地请求。这是一种实现服务器到客户端单向实时通信的方式。SSE 是 HTML5 规范的一部分,并且许多现代浏览器都支持这一技术。
SSE 的主要特点:
- 单项通信:数据只能从服务器推送到客户端,而不是双向的。
- 基于HTTP:SSE 使用普通的 HTTP 连接,与 WebSocket 不同,后者需要更复杂的握手过程。
- 持久连接:一旦客户端与服务器建立连接,该连接会保持开放,直到客户端或服务器关闭它。
- 文本数据:SSE 仅支持文本数据格式,尽管可以发送 JSON 格式的字符串来传输结构化数据。
- 自动重连:如果连接断开,浏览器会尝试重新连接服务器。
- 事件驱动:服务器可以发送不同类型的事件,客户端可以监听这些事件。
SSE 的工作流程:
- 建立连接:客户端发送一个普通的 HTTP 请求到服务器,并在请求头中指定
Accept: text/event-stream
,表示客户端希望使用 SSE。 - 服务器响应:服务器以
text/event-stream
格式响应,保持连接开启。 - 发送事件:服务器可以通过这个开启的连接发送事件,每个事件由一个或多个以
\n\n
分隔的消息组成。 - 客户端处理:客户端有一个
EventSource
对象,用于处理从服务器接收到的消息。
SSE 的应用场景:
- 实时通知:例如,社交网络、邮件服务中的新消息提醒。
- 实时数据更新:股票价格、天气预报等实时数据的展示。
- 日志监控:实时传输服务器日志到客户端,便于监控。
SSE 相对于其他实时通信技术的优势在于它的简单性和易用性,不需要复杂的配置和额外的协议支持。然而,它也有局限性,比如它不支持CORS(跨源资源共享),数据传输是单向的,且仅支持文本数据。
由于这些特点,SSE 特别适合于那些需要从服务器到客户端单向、实时的数据流的场景。
代码示例
下面演示的是在cool-admin如何实现:
服务端
import { CoolController, BaseController } from "@cool-midway/core";
import { Get, Inject } from "@midwayjs/core";
import { Context } from "koa";
import { PluginService } from "../../../plugin/service/info";
import { PassThrough } from "stream";
/**
* 事件流 服务端主动推送
*/
@CoolController()
export class OpenDemoSSEController extends BaseController {
@Inject()
ctx: Context;
@Inject()
pluginService: PluginService;
@Get("/call", { summary: "事件流 服务端主动推送" })
async call() {
// 设置响应头
this.ctx.set("Content-Type", "text/event-stream");
this.ctx.set("Cache-Control", "no-cache");
this.ctx.set("Connection", "keep-alive");
const stream = new PassThrough();
// 发送数据
const send = (data: any) => {
stream.write(`data: ${JSON.stringify(data)}\n\n`);
};
// 获取插件实例
const instance = await this.pluginService.getInstance("ollama");
// 调用chat
const messages = [
{ role: "system", content: "你叫小酷,是个编程助手" },
{ role: "user", content: "用js写个Hello World" }
];
instance.chat(messages, { stream: true }, (res) => {
send(res);
if (res.isEnd) {
this.ctx.res.end();
}
});
this.ctx.status = 200;
this.ctx.body = stream;
}
}
客户端
const axios = require("axios");
const url = "http://127.0.0.1:8001/open/demo/sse/call";
const eventSource = axios.get(url, {
responseType: "stream"
});
eventSource.then((response) => {
// 收到消息
response.data.on("data", (event) => {
console.log("Message received:", event.toString());
});
// 发送结束
response.data.on("end", () => {
console.log("Connection closed");
});
});