Nodejs實(shí)現(xiàn)獲取實(shí)時(shí)數(shù)據(jù)的三種主流方式詳解
本文聊聊前端獲取實(shí)時(shí)數(shù)據(jù)的三種主流方式。
想象一下,我們在網(wǎng)上購物時(shí),經(jīng)常能看到最新的優(yōu)惠信息彈出,或者在社交媒體上看到朋友的最新動(dòng)態(tài)更新。這些都是因?yàn)楹蠖嗽谀叵蛭覀兊捻撁嫱扑土俗钚碌南ⅰD敲矗@背后到底使用了什么樣的技術(shù)呢?主要有三種方式:輪詢(Polling)、網(wǎng)頁套接字(WebSocket) 和服務(wù)器發(fā)送事件(Server-Sent Events, SSE)。下面,我們就來近距離接觸一下它們。
輪詢(Polling)
原理
輪詢的工作方式很像是孩子不斷地問父母“我們到了嗎?”。在這個(gè)比喻中,前端(孩子)定時(shí)向后端(父母)發(fā)送請(qǐng)求來檢查是否有新數(shù)據(jù)(是否到目的地了)。輪詢又分為短輪詢和長輪詢兩種。
- 短輪詢:前端每隔一定時(shí)間就發(fā)送一個(gè)請(qǐng)求,無論數(shù)據(jù)是否更新,后端都立即返回響應(yīng)。
- 長輪詢:前端發(fā)出請(qǐng)求后,后端會(huì)掛起這個(gè)請(qǐng)求,直到有數(shù)據(jù)更新或者超時(shí),才返回響應(yīng)。
優(yōu)缺點(diǎn)
總體上說,輪詢的優(yōu)點(diǎn)在于它簡單易實(shí)現(xiàn),幾乎所有的服務(wù)器和客戶端技術(shù)都能支持。但缺點(diǎn)也很明顯,因?yàn)樗枰粩嗟匕l(fā)送請(qǐng)求,這會(huì)導(dǎo)致大量的網(wǎng)絡(luò)流量,而且實(shí)時(shí)性不高,數(shù)據(jù)更新有延遲。
具體到短輪詢和長輪詢,可以對(duì)它倆再做一個(gè)對(duì)比:
短輪詢
優(yōu)點(diǎn): 實(shí)現(xiàn)簡單,兼容性好。
缺點(diǎn): 頻繁的請(qǐng)求會(huì)增加服務(wù)器負(fù)擔(dān),實(shí)時(shí)性不夠,資源浪費(fèi)較多。
長輪詢
優(yōu)點(diǎn): 相比短輪詢,減少了請(qǐng)求次數(shù),提高了實(shí)時(shí)性。
缺點(diǎn): 實(shí)現(xiàn)相對(duì)復(fù)雜,服務(wù)器端需要維護(hù)掛起的請(qǐng)求,可能會(huì)導(dǎo)致資源占用。
適用場景
輪詢通常用在對(duì)實(shí)時(shí)性要求不是特別高的場景,比如一些后臺(tái)管理系統(tǒng)的消息通知、網(wǎng)頁上的非實(shí)時(shí)統(tǒng)計(jì)信息展示。下面是使用輪詢的一些真實(shí)案例:
- 微信掃碼登錄:微信的掃碼登錄功能使用的是輪詢機(jī)制。當(dāng)你掃描二維碼時(shí),你的手機(jī)上的微信客戶端會(huì)向服務(wù)器發(fā)送信息,然后你的網(wǎng)頁客戶端會(huì)周期性地詢問服務(wù)器,用戶是否已經(jīng)通過手機(jī)確認(rèn)。這里使用輪詢是因?yàn)榈卿洸僮鞑⒉活l繁,且對(duì)實(shí)時(shí)性的要求不需要毫秒級(jí),而且輪詢是一種簡單且兼容性好的實(shí)現(xiàn)方式。
- Facebook的實(shí)時(shí)通知:在Facebook早期,他們使用長輪詢來實(shí)現(xiàn)實(shí)時(shí)通知。用戶的瀏覽器會(huì)開啟一個(gè)到服務(wù)器的長連接,這樣一旦有新通知,服務(wù)器就能立即推送數(shù)據(jù)。這種方式對(duì)于當(dāng)時(shí)的技術(shù)來說是一個(gè)很好的折中方案,因?yàn)樗诓粻奚鄬?shí)時(shí)性的情況下,減少了請(qǐng)求次數(shù)。
代碼示例
這里演示一個(gè)短輪詢。下面我們將要使用Node.js編寫一段后端程序,在這段代碼中我們提供兩個(gè)接口,一個(gè)接口用于接受輪詢、返回最新的消息,另一個(gè)接口則用于更新消息。

后端 poll-server.js 的代碼如下:
const express = require('express');
const app = express();
let message = "No new message";
// 指定public文件夾是靜態(tài)資源的目錄
app.use(express.static("public"));
// 前端要輪詢訪問這個(gè)接口,這個(gè)接口會(huì)返回最新的message
app.get('/poll', (req, res) => {
res.send(message);
message = "No new message";
});
// 在其他地方調(diào)用這個(gè)接口,更新message
app.post('/update', (req, res) => {
message = "Here's a new message!";
res.status(200).send('Message updated');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
前端 poll-client.html 則每隔幾秒鐘就發(fā)送一次請(qǐng)求,代碼如下:
<script type="text/javascript">
setInterval(function() {
fetch('/poll')
.then(response => response.text())
.then(data => {
if (data !== "No new message") {
console.log(data);
}
});
}, 5000); // 每5秒請(qǐng)求一次
</script>
程序啟動(dòng)后,我們調(diào)用一次 /update,前端日志就會(huì)輸出:Here's a new message! ,否則就一直輸出:No new message。
網(wǎng)頁套接字(WebSocket)
原理
WebSocket是一種網(wǎng)絡(luò)通信協(xié)議,它提供了全雙工通信機(jī)制,它允許前后端建立一個(gè)持久的連接,并且雙方都可以通過這個(gè)連接隨時(shí)發(fā)送數(shù)據(jù)。就像打電話,雙方可以隨時(shí)說話和聽對(duì)方說話。
優(yōu)缺點(diǎn)
WebSocket 的優(yōu)點(diǎn)在于它能夠提供真正的實(shí)時(shí)通信,服務(wù)器和客戶端都可以隨時(shí)發(fā)送數(shù)據(jù),而且相比輪詢,它減少了HTTP請(qǐng)求的開銷。但缺點(diǎn)是它的實(shí)現(xiàn)相對(duì)復(fù)雜,在弱網(wǎng)環(huán)境下可能不穩(wěn)定,而且舊版的瀏覽器可能不支持 WebSocket。
適用場景
WebSocket 非常適合需要高實(shí)時(shí)性的場景,比如在線游戲、股票交易平臺(tái)、實(shí)時(shí)聊天應(yīng)用等。下面是使用WebSocket的一些真實(shí)案例:
- Slack:這個(gè)流行的團(tuán)隊(duì)溝通工具使用WebSocket來實(shí)現(xiàn)實(shí)時(shí)消息傳輸。由于團(tuán)隊(duì)成員需要即時(shí)接收信息和通知,WebSocket的全雙工通信機(jī)制提供了幾乎零延遲的數(shù)據(jù)交換。
- 多人在線游戲:許多現(xiàn)代的多人在線游戲使用WebSocket來同步玩家之間的狀態(tài)。這種技術(shù)允許快速的數(shù)據(jù)傳輸,確保了游戲體驗(yàn)的流暢和實(shí)時(shí)互動(dòng)。
代碼示例
在Node.js中使用ws模塊來創(chuàng)建一個(gè)WebSocket服務(wù)器:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3000 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
});
ws.send('something');
});
前端代碼是這樣的:
<script type="text/javascript">
const socket = new WebSocket('ws://localhost:3000');
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
</script>
運(yùn)行這段程序之后,服務(wù)端會(huì)收到前端傳遞的 Hello Server!

前端會(huì)收到服務(wù)端發(fā)送的 something

服務(wù)器發(fā)送事件 (SSE)
原理
服務(wù)器發(fā)送事件(SSE)就像是一個(gè)廣播站,當(dāng)有新節(jié)目(數(shù)據(jù))時(shí),它就會(huì)向所有打開的收音機(jī)(客戶端)發(fā)送信號(hào)。SSE 允許服務(wù)器主動(dòng)向客戶端發(fā)送數(shù)據(jù)流。與WebSocket不同的是,SSE 是單向通信,只能服務(wù)器向客戶端發(fā)送數(shù)據(jù)。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):與WebSocket比,實(shí)現(xiàn)更簡單,只需要簡單的HTTP協(xié)議就可以實(shí)現(xiàn),并且支持自動(dòng)重連;與輪詢比,SSE可以減少服務(wù)器和客戶端之間不必要的通信,服務(wù)器推送的方式更高效。
缺點(diǎn):只支持單向通信(服務(wù)器到客戶端);注意老舊瀏覽器可能不支持SSE(比如IE6、7、8、9)。
適用場景
SSE適用于不需要客戶端向服務(wù)器發(fā)送消息的場景,比如股票價(jià)格的更新、新聞直播、或者任何只需服務(wù)器單向推送的實(shí)時(shí)數(shù)據(jù)顯示。下面是使用SSE的一些真實(shí)案例:
- ChatGPT:作為一個(gè)能夠與用戶進(jìn)行互動(dòng)的聊天機(jī)器人,ChatGPT使用服務(wù)器發(fā)送事件(SSE)來實(shí)現(xiàn)與用戶的實(shí)時(shí)通信。ChatGPT在響應(yīng)用戶輸入時(shí),并不需要雙向?qū)崟r(shí)通信,它主要是接收用戶的請(qǐng)求并返回計(jì)算結(jié)果。SSE可以在現(xiàn)有的HTTP基礎(chǔ)設(shè)施上運(yùn)行,這對(duì)于大規(guī)模服務(wù)而言,在連接管理和服務(wù)器資源分配方面可能更容易維護(hù)和優(yōu)化。
- 股票行情更新:許多金融網(wǎng)站使用SSE來推送實(shí)時(shí)股票價(jià)格和市場數(shù)據(jù)。由于股市信息需要實(shí)時(shí)更新,SSE為用戶提供了一個(gè)不斷流動(dòng)的數(shù)據(jù)源,這樣他們就可以看到最新的市場變化,而不必手動(dòng)刷新頁面。
代碼示例
在Node.js中,我們可以使用如下方式創(chuàng)建一個(gè)SSE服務(wù)器,它每隔1秒向前端推送1條數(shù)據(jù):
const express = require('express');
const app = express();
app.use(express.static("public"));
app.get('/events', function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// 每隔1秒鐘,寫出1條數(shù)據(jù)
setInterval(() => {
res.write(`data: ${new Date().toLocaleTimeString()}\n\n`);
}, 1000);
});
app.listen(3000, () => {
console.log('SSE server running on port 3000');
});
客戶端代碼如下,它接收事件并打印消息:
<script type="text/javascript">
const evtSource = new EventSource('/events');
evtSource.onmessage = function(event) {
console.log('New message:', event.data);
};
</script>
運(yùn)行效果如下:

本文簡要地介紹了三種主要的后端向前端推送數(shù)據(jù)的方式。每種技術(shù)都有自己的適用場景和優(yōu)缺點(diǎn),選擇哪種技術(shù)取決于你的具體需求。不論你是在打造一個(gè)實(shí)時(shí)聊天應(yīng)用,還是一個(gè)股票交易平臺(tái),這些技術(shù)都能幫助你提供更好的用戶體驗(yàn)。
以上就是Nodejs實(shí)現(xiàn)獲取實(shí)時(shí)數(shù)據(jù)的三種主流方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Nodejs獲取實(shí)時(shí)數(shù)據(jù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Windows下快速搭建NodeJS本地服務(wù)器的步驟
本篇文章主要介紹了Windows下快速搭建NodeJS本地服務(wù)器的步驟,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-08-08
解決Node.js使用MySQL出現(xiàn)connect ECONNREFUSED 127.0.0.1:3306的問題
這篇文章主要介紹了解決Node.js使用MySQL出現(xiàn)connect ECONNREFUSED 127.0.0.1:3306報(bào)錯(cuò)的相關(guān)資料,文中將問題描述的很清楚,解決的方法也介紹的很完整,需要的朋友可以參考借鑒,下面來一起看看吧。2017-03-03
Docker平臺(tái)下NodeJs?Puppeteer實(shí)現(xiàn)html轉(zhuǎn)pdf過程示例
這篇文章主要為大家介紹了Docker平臺(tái)下NodeJs?Puppeteer實(shí)現(xiàn)html轉(zhuǎn)pdf過程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
node.js中RPC(遠(yuǎn)程過程調(diào)用)的實(shí)現(xiàn)原理介紹
這篇文章主要介紹了node.js中RPC(遠(yuǎn)程過程調(diào)用)的實(shí)現(xiàn)原理介紹,本文基于一個(gè)簡單的RPC庫nodejs light_rpc實(shí)現(xiàn),需要的朋友可以參考下2014-12-12

