一、 前言

在查看代碼以后發(fā)現(xiàn)這些頻繁的請(qǐng)求是因?yàn)槲覀兊捻?xiàng)目首頁(yè)有一個(gè)待辦任務(wù)數(shù)量和消息提醒數(shù)量的展示,所以之前的同事使用了定時(shí)器,每隔十秒鐘發(fā)送一次請(qǐng)求到后端接口拿數(shù)據(jù),這也就是我們常說(shuō)的輪詢做法。
1. 輪詢的缺點(diǎn)
我們都知道輪詢的缺點(diǎn)有幾種:
資源浪費(fèi):
用戶體驗(yàn):
2. websocket的缺點(diǎn)
那么有沒(méi)有替代輪詢的做法呢? 聰明的同學(xué)肯定會(huì)第一時(shí)間想到用websocket
,但是在目前這個(gè)場(chǎng)景下我覺(jué)得使用websocket
是顯得有些笨重。我從以下這幾方面對(duì)比:
客戶端實(shí)現(xiàn):
適用場(chǎng)景:
實(shí)現(xiàn)復(fù)雜性:
瀏覽器支持:
服務(wù)器資源消耗:
二、 詳細(xì)對(duì)比
對(duì)于這三者的詳細(xì)區(qū)別,你可以參考下面我總結(jié)的表格:
以下是 WebSocket、輪詢和 SSE 的對(duì)比表格:
特性 | WebSocket | 輪詢Polling | Server-Sent Events (SSE) |
---|
定義 | 全雙工通信協(xié)議,支持服務(wù)器和客戶端之間的雙向通信。 | 客戶端定期向服務(wù)器發(fā)送請(qǐng)求以檢查更新。 | 服務(wù)器向客戶端推送數(shù)據(jù)的單向通信協(xié)議。 |
實(shí)時(shí)性 | 高,服務(wù)器可以主動(dòng)推送數(shù)據(jù)。 | 低,依賴客戶端定時(shí)請(qǐng)求。 | 高,服務(wù)器可以主動(dòng)推送數(shù)據(jù)。 |
開(kāi)銷 | 相對(duì)較高,需要建立和維護(hù)持久連接。 | 較低,但頻繁請(qǐng)求可能導(dǎo)致高網(wǎng)絡(luò)和服務(wù)器開(kāi)銷。 | 相對(duì)較低,只需要一個(gè)HTTP連接,服務(wù)器推送數(shù)據(jù)。 |
瀏覽器支持 | 現(xiàn)代瀏覽器支持,需要額外的庫(kù)來(lái)支持舊瀏覽器。 | 所有瀏覽器支持。 | 現(xiàn)代瀏覽器支持良好,舊瀏覽器可能需要polyfill。 |
實(shí)現(xiàn)復(fù)雜性 | 高,需要處理連接的建立、維護(hù)和關(guān)閉。 | 低,只需定期發(fā)送請(qǐng)求。 | 中等,只需要處理服務(wù)器推送的數(shù)據(jù)。 |
數(shù)據(jù)格式 | 支持二進(jìn)制和文本數(shù)據(jù)。 | 通常為JSON或XML。 | 僅支持文本數(shù)據(jù),通常為JSON。 |
控制流 | 客戶端和服務(wù)器都可以控制消息發(fā)送。 | 客戶端控制請(qǐng)求發(fā)送頻率。 | 服務(wù)器完全控制數(shù)據(jù)推送。 |
安全性 | 需要wss://(WebSocket Secure)來(lái)保證安全。 | 需要https://來(lái)保證請(qǐng)求的安全。 | 需要SSE通過(guò)HTTPS提供,以保證數(shù)據(jù)傳輸?shù)陌踩?/td> |
適用場(chǎng)景 | 需要雙向交互的應(yīng)用,如聊天室、實(shí)時(shí)游戲。 | 適用于更新頻率不高的場(chǎng)景,如輪詢郵箱。 | 適用于服務(wù)器到客戶端的單向數(shù)據(jù)流,如股票價(jià)格更新。 |
跨域限制 | 默認(rèn)不支持跨域,需要服務(wù)器配置CORS。 | 默認(rèn)不支持跨域,需要服務(wù)器配置CORS。 | 默認(rèn)不支持跨域,需要服務(wù)器配置CORS。 |
重連機(jī)制 | 客戶端可以實(shí)現(xiàn)自動(dòng)重連邏輯。 | 需要客戶端實(shí)現(xiàn)重連邏輯。 | 客戶端可以監(jiān)聽(tīng)連接關(guān)閉并嘗試重連。 |
服務(wù)器資源 | 較高,因?yàn)樾枰S護(hù)持久連接。 | 較低,但頻繁的請(qǐng)求可能增加服務(wù)器負(fù)擔(dān)。 | 較低,只需要維護(hù)一個(gè)HTTP連接。 |
這個(gè)表格概括了 WebSocket、輪詢和 SSE 在不同特性上的主要對(duì)比點(diǎn)。每種技術(shù)都有其適用的場(chǎng)景和限制,選擇合適的技術(shù)需要根據(jù)具體的應(yīng)用需求來(lái)決定。
三、 SSE(Server-Sent Events)介紹
我們先來(lái)簡(jiǎn)單了解一下什么是Server-Sent Events
?
Server-Sent Events (SSE)
是一種允許服務(wù)器主動(dòng)向客戶端瀏覽器推送數(shù)據(jù)的技術(shù)。它基于 HTTP 協(xié)議
,但與傳統(tǒng)的 HTTP 請(qǐng)求-響應(yīng)模式不同,SSE 允許服務(wù)器在建立連接后,通過(guò)一個(gè)持久的連接不斷地向客戶端發(fā)送消息。
工作原理
建立連接:
服務(wù)器推送消息:
客戶端接收消息:
連接管理:
著名的計(jì)算機(jī)科學(xué)家林納斯·托瓦茲(Linus Torvalds) 曾經(jīng)說(shuō)過(guò):talk is cheap ,show me your code
。
我們直接上代碼看看效果:
java代碼
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("platform/todo")
public class TodoSseController {
private final ExecutorService executor = Executors.newCachedThreadPool();
@GetMapping("/endpoint")
public SseEmitter refresh(HttpServletRequest request) {
final SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
executor.execute(() -> {
try {
while (true) { // 無(wú)限循環(huán)發(fā)送事件,直到連接關(guān)閉
// 發(fā)送待辦數(shù)量更新
emitter.send(SseEmitter.event().data(5));
// 等待5秒
TimeUnit.SECONDS.sleep(5);
}
} catch (IOException e) {
emitter.completeWithError(e);
} catch (InterruptedException e) {
// 當(dāng)前線程被中斷,結(jié)束連接
Thread.currentThread().interrupt();
emitter.complete();
}
});
return emitter;
}
}
前端代碼
beforeCreate() {
const eventSource = new EventSource('/platform/todo/endpoint');
eventSource.onmessage = (event) => {
console.log("evebt:",event)
};
eventSource.onerror = (error) => {
console.error('SSE error:', error);
eventSource.close();
};
this.$once('hook:beforeDestroy', () => {
if (eventSource) {
eventSource.close();
}
});
},
改造后的效果


可以看到,客戶端只發(fā)送了一次http請(qǐng)求,后續(xù)所有的返回結(jié)果都可以在event.data
里面獲取,先不談性能,對(duì)于有強(qiáng)迫癥的同學(xué)是不是一個(gè)很大改善呢?
總結(jié)
雖然 SSE
(Server-Sent Events)因其簡(jiǎn)單性和實(shí)時(shí)性在某些場(chǎng)景下提供了顯著的優(yōu)勢(shì),比如在需要服務(wù)器向客戶端單向推送數(shù)據(jù)時(shí),它能夠以較低的開(kāi)銷維持一個(gè)輕量級(jí)的連接,但 SSE 也存在一些局限性。例如,它不支持二進(jìn)制數(shù)據(jù)傳輸,這對(duì)于需要傳輸圖像、視頻或復(fù)雜數(shù)據(jù)結(jié)構(gòu)的應(yīng)用來(lái)說(shuō)可能是一個(gè)限制。此外,SSE 只支持文本格式的數(shù)據(jù)流,這可能限制了其在某些數(shù)據(jù)傳輸場(chǎng)景下的應(yīng)用。還有,SSE 的兼容性雖然在現(xiàn)代瀏覽器中較好,但在一些舊版瀏覽器中可能需要額外的 polyfill 或者降級(jí)方案。
考慮到這些優(yōu)缺點(diǎn),我們?cè)谶x擇數(shù)據(jù)通信策略時(shí),應(yīng)該基于項(xiàng)目的具體需求和上下文來(lái)做出決策。如果項(xiàng)目需要雙向通信或者傳輸二進(jìn)制數(shù)據(jù),WebSocket 可能是更合適的選擇。
如果項(xiàng)目的數(shù)據(jù)更新頻率不高,或者只需要客戶端偶爾查詢服務(wù)器狀態(tài),傳統(tǒng)的輪詢可能就足夠了。
而對(duì)于需要服務(wù)器頻繁更新客戶端數(shù)據(jù)的場(chǎng)景,SSE 提供了一種高效的解決方案。
總之,選擇最合適的技術(shù)堆棧需要綜合考慮項(xiàng)目的需求、資源限制、用戶體驗(yàn)和未來(lái)的可維護(hù)性。