什么是SSE
SSE(Server-Sent Events)是一Web API,允许服务器将事件数据推送到客户端,以实现服务器向客户端的单向通信。 它建立在HTTP协议之上,并通过HTTP连接的持打开来实现长轮询。 这意味着,一旦连接建立,客户端和服务器之间就可以实时交互,服务器可以随时向客户端提供新数据,不需要客户端首先请求数据。
下面是我用fetch写的一个SSE请求的Demo,服务端用的Nest
服务端代码:
@Post('completion')
@Sse('completion')
async completion(
@Body() obj: any,
@Headers() hearder: any,
): Promise<Observable<MessageEvent> | string> {
// 验证会员等级和回答次数
const token = this.chatService.extractTokenFromHeader(
hearder.authorization,
);
const { id } = await this.jwtService.verifyAsync(token, {
secret: jwtConstants.secret,
});
const user = await this.chatService.getBuyChatTimes(id);
if (user && user.chat_times < user.buy_chat_times) {
if (obj.messages.length > 0) {
obj.messages.forEach((item: any) => {
delete item.time;
});
}
const response = this.httpService.post(CHAT_URL, obj, {
responseType: 'stream', // 设置响应类型为流
headers: {
'Content-Type': 'application/json',
},
});
const chatStream$ = new Observable<string>((observer) => {
response.subscribe((response) => {
response.data.on('data', (chunk: string) => {
const message = chunk.toString();
observer.next(message); // 将从第三方接口返回的数据流实时传递给前端
response.data.on('end', () => {
observer.complete(); // 在数据流结束时发送 complete 通知
});
});
});
});
return chatStream$.pipe(map((data) => ({ data: { data } })));
}
const res = new Observable<string>((observer) => {
observer.next('{"content":"今日查询次数已达上限!"}');
observer.complete();
});
return res.pipe(map((data) => ({ data: { data } })));
}
客户端代码:
const obj = {
"messages": [
{
"role": "system",
"content": "欢迎回来!您想聊些什么?",
"time": 1690275348057
},
{
"role": "user",
"content": "写一篇关于父亲的作文",
"time": 1690337646761
},
],
"temperature": 1.2,
"max_tokens": 200
}
// const res = await request('/user/info')
const baseUrl = 'http://localhost:7003/api/chat/completion';
const eventFetch = new FetchEventSource()
eventFetch.stopFetchEvent()
eventFetch.startFetchEvent(baseUrl, obj, res => {
const regex = /\\"content\\":\\"(.*?)\\"/g;
let match;
while ((match = regex.exec(res)) !== null) {
replyMsg += match[1]
.replace(/\\n/g, "\n")
.replace(/(\\u[0-9a-fA-F]{4})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1.substring(2), 16));
})
.replace(/\\/g, "");
}
// console.log(replyMsg, 'replyMsg');
}, () => {
console.log('end', replyMsg);
}, error => {
console.log(error, 'error');
})
class FetchEventSource {
constructor() {
this.abortController = new AbortController() || null;
}
startFetchEvent(url, body, onMessage, onEnd, onError, headers = {}) {
const fetchOptions = {
method: 'POST',
body: JSON.stringify(body),
headers: Object.assign({}, {
'Content-Type': 'application/json',
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXNzd29yZCI6IjEyMzQ1NiIsInBob25lIjoiMTg4MTkzMTA0ODgiLCJpZCI6MTgsImlhdCI6MTY5MDM0MDUwNiwiZXhwIjoxNjkwNDI2OTA2fQ.iol2hbgdGnmbRtsxDHn-NbXH1fgUapZpX4Wg6FDDsw0'
}),
signal: this.abortController.signal,
};
fetch(url, fetchOptions).then((response) => {
const reader = response.body.getReader();
reader.read().then(function processResult(result) {
if (result.done) {
onEnd()
return;
}
const decoder = new TextDecoder();
const receivedString = decoder.decode(result.value, { stream: true });
oneMssage(receivedString)
return reader.read().then(processResult);
});
return response;
}).catch(() => {
this.eventController.abort();
onError({ code: 201, message: '服务器异常' })
});
}
stopFetchEvent() {
if (this.eventController) {
this.eventController.abort();
this.eventController = null;
}
}
}
在上面的代码中,我封装FetchEventSource类,这个类是一个支持流式请求的工具类,用Fetch + AbortController实现的Server-SentEvents(SSE)的流式请求,用于简化实时数据的推送和更新。对外提供了startFetchEvent和stopFetchEvent两个方法,用于启动和停止一个SSE的流式请求。该类支持自定义请求头和请求体,最终的效果就如下面这样了
这边用到了
AbortController
来取消网络请求,其他参数:
url
: 请求的URL地址body
: 请求体数据对象,将会被转成 JSON 格式onMessage
: 服务端有数据返回时的回调函数
onEnd
: 请求结束时的回调函数onError
:请求错误时的回调函数headers
:请求头信息,可选参数,主要是传token
接下来发送 POST 请求并监听响应数据的处理过程是:
- 使用
fetch
方法发送请求,将参数fetchOptions
传递给它; - 请求发送成功后,获取响应对象
response
; - 从应对象
response
中获取body
的读取器reader
; - 通过
reader.read()
方法获取响应的一部分数据,并对的result
执行processResult
处理。processResult
逐步将获取的数据传递到回调函数onMessage
中; - 如果读取完毕,执行
onEnd
,请求结束。
总体来说,这个类的作用是发送HTTP POST请求并实时监听服务端返回的内容,这一般是用于实现实时消息推送的功能。