HTTP/2 + SSE 能否完全替代 Websocket嗎?
當(dāng)前位置:點(diǎn)晴教程→知識(shí)管理交流
→『 技術(shù)文檔交流 』
1. 什么是 SSE(Server-Sent Events)服務(wù)器發(fā)送事件 (SSE) 是一種標(biāo)準(zhǔn)化協(xié)議,允許 Web 服務(wù)器將數(shù)據(jù)推送到客戶端而無(wú)需使用替代機(jī)制,例如: ping、長(zhǎng)輪詢 (Long Polling) 或 WebSocket。 ![]() 服務(wù)器發(fā)送事件(SSE)是 HTML5 公布的一種服務(wù)器向?yàn)g覽器客戶端發(fā)起數(shù)據(jù)傳輸?shù)募夹g(shù)。一旦創(chuàng)建了初始連接,事件流將保持打開(kāi)狀態(tài) 直到客戶端關(guān)閉。該技術(shù)通過(guò)傳統(tǒng)的 HTTP 發(fā)送,并具有 WebSocket 缺乏的各種功能,例如:自動(dòng)重新連接、 事件 ID 以及 發(fā)送任意事件 的能力。 ?? SSE 本身并沒(méi)有提供自動(dòng)重連的機(jī)制,其所謂的自動(dòng)重連特性是指 ` 瀏覽器自動(dòng)處理與服務(wù)器的連接斷開(kāi)并嘗試重新連接的過(guò)程 `。當(dāng)然開(kāi)發(fā)者也可以通過(guò) `eventSource.retry = 5000` 手動(dòng)重連。 SSE 就是利用服務(wù)器向客戶端聲明,接下來(lái)要發(fā)送的是流信息(streaming),會(huì)連續(xù)不斷地發(fā)送過(guò)來(lái)。這時(shí),客戶端不會(huì)關(guān)閉連接,會(huì)一直等著服務(wù)器發(fā)過(guò)來(lái)的新的數(shù)據(jù)流,可以類比視頻流。SSE 就是利用這種機(jī)制,使用流信息向?yàn)g覽器推送信息。其基于 HTTP 協(xié)議,目前除了 IE/Edge,其他瀏覽器都支持。 Content-Type: text/event-stream// 必須是 UTF-8 編碼的文本,流本質(zhì)就是下載Cache-Control: no-cacheConnection: keep-alive 使用 SSE 可以 顯著節(jié)省便攜式設(shè)備的帶寬和電池壽命,并且可以與現(xiàn)有的基礎(chǔ)設(shè)施配合使用,因?yàn)槠渲苯油ㄟ^(guò) HTTP 協(xié)議運(yùn)行而無(wú)需像 WebSockets 那樣進(jìn)行連接升級(jí)。 2.SSE 如何借力 HTTP2 補(bǔ)齊與 WebSocket 的短板由于 SSE 是基于 HTTP 的,其天然適配于 HTTP/2,這樣 SSE 就可以集兩者之長(zhǎng):HTTP/2 可以基于多路復(fù)用流形成一個(gè)高效傳輸層,同時(shí) SSE 給應(yīng)用提供了 API 使之能夠進(jìn)行推送 。 ?? 流就是一個(gè)獨(dú)立的、在客戶端服務(wù)器之間的 HTTP/2 連接上雙向的幀序列,其最重要的一個(gè)特征就是 ` 單個(gè) HTTP/2 連接可以包含多個(gè)并發(fā)開(kāi)啟的流 `,其中每個(gè)端點(diǎn)都交錯(cuò)著來(lái)自多個(gè)流的幀。 ![]() 假如應(yīng)用使用 HTTP/1 傳輸,此時(shí) NetWork 選項(xiàng)卡可能會(huì)有如下的輸出: ![]() 瀏覽器會(huì)并行打開(kāi)多個(gè) HTTP/1.x 連接來(lái)加速頁(yè)面加載,而 不同的瀏覽器針對(duì)同一域名并發(fā)打開(kāi)的連接數(shù)量有不同的限制,基本上都會(huì)支持 6 個(gè)左右不同的連接。 為了克服這個(gè)限制,類似于 域名分片的技術(shù)就被用來(lái)將資源分布在多個(gè)域名上。這些技術(shù)(可以將其認(rèn)為是非法入侵)包括 JavaScript 和 CSS 文件、圖像和資源內(nèi)聯(lián),在 HTTP/2 世界中反而適得其反。這可能是遷移到 HTTP/2 時(shí)受到的最主要的影響了,即 消除多年以來(lái)所做的優(yōu)化。 當(dāng)使用 HTTP/2 的時(shí)候,NetWork 中會(huì)看到瀏覽器使用單個(gè)多路復(fù)用的連接,帶來(lái)更快的加載時(shí)間。 ![]() 而 SSE 是基于 HTTP 的,這意味著使用 HTTP/2 的時(shí)候,不僅僅可以在一個(gè) TCP 連接上交錯(cuò)多個(gè) SSE 流,同時(shí)還可以將多個(gè) SSE 流(服務(wù)器到客戶端推送)與多個(gè)客戶端請(qǐng)求(客戶端到服務(wù)器)交錯(cuò)。 const http2 = require('http2');const fs = require('fs');// 創(chuàng)建 HTTP/2 服務(wù)器const server = http2.createSecureServer({ key: fs.readFileSync('server-key.pem'), cert: fs.readFileSync('server-cert.pem')});server.on('stream', (stream, headers) => { const path = headers[':path']; const method = headers[':method']; if (path === '/events' && method === 'GET') { // 設(shè)置響應(yīng)頭 stream.respond({ 'content-type': 'text/event-stream', ':status': 200 }); // 發(fā)送一條歡迎消息 stream.write(`data: Welcome! Current time is ${new Date().toISOString()}\n\n`); // 每 5 秒發(fā)送一條消息 const intervalId = setInterval(() => { stream.write(`data: Current time is ${new Date().toISOString()}\n\n`); }, 5000); // 清理工作 stream.on('close', () => { clearInterval(intervalId); }); } elseif (path === '/send-data' && method === 'POST') { // 客戶端通過(guò) fetch('https://localhost:3000/send-data') 發(fā)送消息到服務(wù)端 let body = ''; stream.on('data', chunk => { body += chunk.toString(); }); stream.on('end', () => { console.log('Received data:', body); stream.respond({':status': 200}); stream.end(JSON.stringify({ status: 'success', data: body})); }); } else { stream.respond({':status': 404}); stream.end(); }});// 監(jiān)聽(tīng)端口server.listen(3000, () => { console.log('Server running at https://localhost:3000/');}); // 下面是瀏覽器連接 HTTP/2 的示例const eventSource = new EventSource('https://localhost:3000/events');// 監(jiān)聽(tīng)消息事件eventSource.onmessage = function(event) { const messageDiv = document.getElementById('messages'); const newMessage = document.createElement('div'); newMessage.textContent = `New message: ${event.data}`; messageDiv.appendChild(newMessage);};// 監(jiān)聽(tīng)連接打開(kāi)事件eventSource.onopen = function(event) { console.log('Connection opened');};// 監(jiān)聽(tīng)錯(cuò)誤事件eventSource.onerror = function(event) { if (eventSource.readyState === EventSource.CLOSED) { console.log('Connection closed'); } else { console.log('Error occurred, attempting to reconnect...'); }};// 發(fā)送 HTTP/2 請(qǐng)求document.getElementById('sendButton').addEventListener('click', () => { fetch('https://localhost:3000/send-data', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({message: 'Hello, server!'}) }) .then(response => response.json()) .then(data => { console.log('Response:', data); }) .catch(error => { console.error('Error:', error); });}); 比如上面的 http2 代碼示例:
有了 HTTP/2 和 SSE 就可以使用一個(gè)純 HTTP 雙向連接,加之使用簡(jiǎn)單的 API 使應(yīng)用代碼注冊(cè)多個(gè)服務(wù)器推送。 雙向能力的缺失一直是 SSE 對(duì)比 Websocket 時(shí)最主要的短板。有了 HTTP/2 就彌補(bǔ)了這個(gè)短板,從而為跳過(guò) Websocket 并堅(jiān)持使用基于 HTTP 的機(jī)制提供了可能。 3.HTTP/2 + SSE 能完全替代 Websocket 嗎?答案是 No,主要是 Websocket 已經(jīng)被大量應(yīng)用,同時(shí)在某些特定應(yīng)用場(chǎng)景下,其底層設(shè)計(jì)致力于雙向能力,擁有較少的負(fù)載的優(yōu)勢(shì)就會(huì)體現(xiàn)出來(lái)。假設(shè)需要在雙端之間交互大吞吐量的消息,其中上下流動(dòng)的消息量大致差不多(比如,需要保持所有玩家同步的大型多人在線游戲),這種場(chǎng)景下 Websocket 可能會(huì)是更好的選擇。 如果考慮像是展示 實(shí)時(shí)市場(chǎng)新聞、市場(chǎng)數(shù)據(jù)、聊天應(yīng)用 等場(chǎng)景的時(shí)候,依賴 HTTP/2 + SSE 會(huì)提供高效的雙向通信通道并保有留在 HTTP 世界的大量?jī)?yōu)勢(shì):
因此 HTTP/2 + SSE 能否完全替代 Websocket 的結(jié)論是:
Websocket 技術(shù)可能會(huì)繼續(xù)使用,但是 SSE 和其 EventSource API 同 HTTP/2 的能力相結(jié)合可以在多數(shù)場(chǎng)景下達(dá)到同樣的效果,而且會(huì)更簡(jiǎn)單。 參考資料https://zhuanlan.zhihu.com/p/37365892 https://blog.csdn.net/cnweike/article/details/116056371 https://www.infoq.com/articles/websocket-and-http2-coexist/ https://stackoverflow.com/questions/48344634/why-do-we-need-sse-when-we-have-http2-bidirectional-streaming 閱讀原文:原文鏈接 該文章在 2025/5/6 12:43:08 編輯過(guò) |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |