當(dāng)涉及到推送數(shù)據(jù)時,人們首先會想到 WebSocket。
的確,WebSocket 允許雙向通信,可以自然地用于服務(wù)器到瀏覽器的消息推送。
然而,如果只需要單向的消息推送,HTTP 通過服務(wù)器發(fā)送的事件也有這種功能。
WebSocket 的通信過程如下:
首先,通過 HTTP 切換協(xié)議。服務(wù)器返回 101 狀態(tài)碼后,協(xié)議切換成功。
然后,開始以 WebSocket 格式的數(shù)據(jù)通信,任意一方都可以隨時向另一方推送消息。
至于 HTTP 中的服務(wù)器發(fā)送的事件:
服務(wù)器返回的 Content-Type
是 text/event-stream
,這是一種可以多次返回內(nèi)容的流。
服務(wù)器發(fā)送的事件通過這種類型的消息隨時推送數(shù)據(jù)。
你可能是第一次聽說 SSE,但你已經(jīng)使用過基于它的應(yīng)用程序。
例如,你使用的 CI/CD 平臺會實時打印日志。
那么它如何實時傳輸構(gòu)建日志呢?
它需要分次傳輸,SSE 通常用于以這種方式推送數(shù)據(jù)。
另一個例子是 ChatGPT。它在回答問題時不會一次給你所有答案,而是逐步分塊加載。
這也是基于 SSE 的。
現(xiàn)在我們已經(jīng)知道 SSE 是什么以及它的應(yīng)用,讓我們自己實現(xiàn)它。
創(chuàng)建一個 Nest 項目。
npx nest new sse-test
運行它:
訪問 http://localhost:3000 會顯示“Hello World”,表示服務(wù)器運行成功。
然后在 AppController 中添加一個流接口。
這里沒有用 @Get
、@Post
等裝飾器進(jìn)行標(biāo)識,而是 @Sse
裝飾器表示這是一個事件流類型的接口。
@Sse('stream')
stream() {
return new Observable((observer) => {
observer.next({ data: { msg: 'aaa'} });
setTimeout(() => {
observer.next({ data: { msg: 'bbb'} });
}, 2000);
setTimeout(() => {
observer.next({ data: { msg: 'ccc'} });
}, 5000);
});
}
返回的是 Observable 對象,然后在內(nèi)部使用 observer.next 返回消息。可以返回任何 JSON 數(shù)據(jù)。我們首先返回 aaa,2 秒后返回 bbb,5 秒后返回 ccc。然后創(chuàng)建一個前端頁面:創(chuàng)建一個 React 項目。
npx create-react-app --template=typescript sse-test-frontend
在 App.tsx
中編寫以下代碼:
import { useEffect } from 'react';
function App() {
useEffect(() => {
const eventSource = new EventSource('http://localhost:3000/stream');
eventSource.onmessage = ({ data }) => {
console.log('New message', JSON.parse(data));
};
}, []);
return (
<div>hello</div>
);
}
export default App;
這個 EventSource 是瀏覽器的原生 API,用于獲取 SSE 接口的響應(yīng)。它會將每個消息傳入回調(diào)函數(shù) onmessage
中。
我們在 Nest 服務(wù)中啟用跨域支持。
然后刪除 react 項目中的 index.tsx 文件中的這幾行代碼,因為它們會導(dǎo)致額外的渲染:
執(zhí)行 npm run start
因為 3000 端口被占用,它將在 3001 上運行:
訪問瀏覽器:
看到響應(yīng)了嗎?
這就是服務(wù)器發(fā)送的事件。
在 devtools
中,你可以看到響應(yīng)的 Content-Type
是 text/event-stream
。
然后在 EventStream 中,你可以看到接收到的每條消息。
通過這種方式,服務(wù)器可以隨時向網(wǎng)頁推送消息。
它的兼容性如何?
你可以在 MDN 上看到。
除了 IE 和 Edge 外,與其他瀏覽器沒有兼容問題。
一般來說,安全使用。
它可以在哪里使用?
服務(wù)器發(fā)送的事件
特別適合只需要服務(wù)器端推送的場景。
例如日志的實時推送。
讓我們測試一下:
“tail -f”命令允許你實時查看文件的最新內(nèi)容。
我們使用 child_process 模塊的 exec 函數(shù)來執(zhí)行這個命令,然后監(jiān)聽它的 stdout 輸出。
const { exec } = require("child_process");
const childProcess = exec('tail -f ./log');
childProcess.stdout.on('data', (msg) => {
console.log(msg);
});
使用 node 執(zhí)行它。
然后添加一個 SSE 接口。
@Sse('stream2')
stream2() {
const childProcess = exec('tail -f ./log');
return new Observable((observer) => {
childProcess.stdout.on('data', (msg) => {
observer.next({ data: { msg: msg.toString() }});
})
});
檢測到新數(shù)據(jù)后,返回到瀏覽器。
瀏覽器連接到這個新接口:
測試如下:
可以看到瀏覽器已經(jīng)接收到實時日志。
許多構(gòu)建日志都是通過 SSE 實時推送的。
日志和類似的東西只是文本,但是如果是二進(jìn)制數(shù)據(jù)呢?
在 Node.js 中,二進(jìn)制數(shù)據(jù)存儲在 Buffer 中。
const { readFileSync } = require("fs");
const buffer = readFileSync('./package.json');
console.log(buffer);
Buffer 有一個 toJSON 方法:
這可以通過 SSE 接口返回嗎?
試一下:
@Sse('stream3')
stream3() {
return new Observable((observer) => {
const json = readFileSync('./package.json').toJSON();
observer.next({ data: { msg: json }});
});
}
的確可以。
換句話說,基于 SSE,除了可以推送文本,還可以推送任何二進(jìn)制數(shù)據(jù)。
概括
可以使用 WebSocket 或 HTTP 的服務(wù)器發(fā)送事件(SSE)從服務(wù)器推送實時數(shù)據(jù)。
通過在 HTTP 響應(yīng)中返回一個 Content-Type 為 text/event-stream 的頭,可以通過流多次發(fā)送消息。
傳輸?shù)膬?nèi)容是 JSON 格式,可以用來傳輸文本或二進(jìn)制內(nèi)容。
我們使用 Nest 實現(xiàn)了 SSE 接口。方法使用 @Sse
裝飾器進(jìn)行注釋,它返回一個 Observable 對象??梢允褂?observer.next
隨時返回數(shù)據(jù)。
在前端,使用 EventSource 的 onmessage
來接收消息。
這個 API 在除 IE 和 Edge 外的其他瀏覽器有很好的兼容性,可以安全使用。
它有各種應(yīng)用,如內(nèi)部消息傳遞、構(gòu)建日志的實時顯示和 chatgpt
的消息響應(yīng)。文章來源:http://www.zghlxwxcb.cn/news/detail-800504.html
當(dāng)遇到需要消息推送的場景時,考慮使用服務(wù)器發(fā)送的事件而不是 WebSocket。文章來源地址http://www.zghlxwxcb.cn/news/detail-800504.html
到了這里,關(guān)于服務(wù)器推送數(shù)據(jù)你還在用 WebSocket么?的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!