0%

页面实时获取数据

页面实时获取数据方案全解析:从轮询到 WebSocket 与 SSE

在实时交互场景中(如即时聊天、实时数据监控、在线协作工具),前端需要实时获取后端数据。传统的 “请求 - 响应” 模式无法满足实时性需求,因此衍生出轮询、WebSocket、SSE 等技术方案。本文将详细解析三种方案的原理、实现、优缺点及适用场景,帮助开发者选择合适的实时数据获取方式。

方案一:轮询(Polling)

轮询是最直观的实时数据获取方式,通过前端定时发送请求,后端返回最新数据,实现 “准实时” 效果。

原理

  • 前端:使用定时器(如setInterval)每隔固定时间(如 1 秒)向后端发送 HTTP 请求;
  • 后端:收到请求后,立即返回当前最新数据(无论是否有更新);
  • 特点:基于 HTTP 协议,无需特殊协议支持,实现简单。

实现示例

(1)前端代码
1
2
3
4
5
6
7
8
9
10
// 每1秒发送一次请求
setInterval(() => {
fetch("/api/realtime-data")
.then(response => response.json())
.then(data => {
console.log("最新数据:", data);
// 更新页面UI
})
.catch(error => console.error("请求失败:", error));
}, 1000);
(2)后端代码(Java Servlet)
1
2
3
4
5
6
7
8
9
10
@WebServlet("/api/realtime-data")
public class RealtimeDataServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("application/json;charset=UTF-8");
// 模拟实时数据(如当前在线人数、温度等)
String data = "{\"onlineUsers\":" + (int)(Math.random() * 100) + ", \"timestamp\":" + System.currentTimeMillis() + "}";
response.getWriter().write(data);
}
}

优缺点

优点:
  • 实现简单:无需特殊协议或后端配置,前端仅需定时器 + HTTP 请求;
  • 兼容性好:支持所有浏览器和服务器,无技术门槛。
缺点:
  • 资源浪费:无论数据是否更新,前端都会定时发送请求,无效请求占比高(如数据 10 秒才更新一次,1 秒一次的请求有 9 次无效);
  • 实时性差:数据更新延迟取决于轮询间隔(间隔 1 秒则最大延迟 1 秒);
  • 服务器压力大:高频请求会占用大量连接和带宽资源。

适用场景

  • 实时性要求低的场景(如数据 5-10 分钟更新一次);
  • 简单的小型应用(如个人博客的访问量统计);
  • 对兼容性要求极高,无法使用 WebSocket/SSE 的环境。

方案二:WebSocket(全双工实时通信)

WebSocket 是 HTML5 定义的全双工通信协议,通过一次 TCP 连接实现客户端与服务器的双向实时通信,摆脱了 HTTP “请求 - 响应” 模式的限制。

原理

  • 连接建立:客户端通过ws://wss://协议发送握手请求,服务器响应后建立持久 TCP 连接;
  • 双向通信:连接建立后,服务器可主动向客户端推送数据,客户端也可随时向服务器发送数据(全双工);
  • 特点:基于 TCP 协议,连接一旦建立,无需重复握手,数据传输高效。

实现示例

(1)前端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE html>
<html>
<body>
<div id="messageList"></div>
<script>
// 建立WebSocket连接(后端端点为/ws)
const socket = new WebSocket("ws://localhost:8080/ws");

// 连接成功时触发
socket.onopen = (event) => {
console.log("WebSocket连接已建立");
// 可向服务器发送初始消息
socket.send("客户端已就绪");
};

// 收到服务器推送的消息时触发
socket.onmessage = (event) => {
const messageList = document.getElementById("messageList");
const message = document.createElement("p");
message.textContent = "服务器推送:" + event.data;
messageList.appendChild(message);
};

// 连接关闭时触发
socket.onclose = (event) => {
console.log("WebSocket连接已关闭,代码:" + event.code);
// 可尝试重连
};

// 连接出错时触发
socket.onerror = (error) => {
console.error("WebSocket错误:", error);
};
</script>
</body>
</html>
(2)后端代码(Java WebSocket)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

// 定义WebSocket端点路径
@ServerEndpoint("/ws")
public class WebSocketServer {
// 存储所有连接的会话(线程安全)
private static final CopyOnWriteArraySet<Session> sessions = new CopyOnWriteArraySet<>();

// 新连接建立时触发
@OnOpen
public void onOpen(Session session) {
System.out.println("新客户端连接,会话ID:" + session.getId());
sessions.add(session);
}

// 收到客户端消息时触发
@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("收到客户端消息:" + message);
// 向所有客户端广播消息(如实时聊天场景)
for (Session s : sessions) {
if (s.isOpen()) {
s.getBasicRemote().sendText("服务器收到:" + message);
}
}
}

// 连接关闭时触发
@OnClose
public void onClose(Session session) {
System.out.println("客户端断开连接,会话ID:" + session.getId());
sessions.remove(session);
}
}

优缺点

优点:
  • 实时性强:服务器可主动推送数据,无延迟(毫秒级响应);
  • 高效性:一次 TCP 连接复用到底,避免 HTTP 重复握手(三次握手、四次挥手)的开销;
  • 双向通信:支持客户端与服务器双向实时交互(如即时聊天、协作编辑)。
缺点:
  • 实现复杂:需后端支持 WebSocket 协议(如 Tomcat 7+、Spring WebSocket),前端需处理连接状态(断开重连等);
  • 兼容性限制:老式浏览器(如 IE 10 以下)不支持,需降级方案;
  • 资源占用:长连接会占用服务器 TCP 连接资源,高并发场景需优化(如连接池、心跳检测)。

适用场景

  • 实时双向交互场景(如即时通讯、在线游戏、协作工具);
  • 高实时性要求场景(如股票行情、实时监控数据);
  • 频繁数据交互场景(避免轮询的无效请求浪费)。

方案三:SSE(Server-Sent Events,服务器推送事件)

SSE 是 HTML5 定义的基于 HTTP 的单向通信协议,允许服务器通过长连接主动向客户端推送数据,客户端无需发送请求。

原理

  • 连接建立:客户端通过普通 HTTP 请求(GET)连接后端 SSE 端点,服务器返回特殊响应头(Content-Type: text/event-stream),维持长连接;
  • 单向通信:仅服务器可向客户端推送数据,客户端无法主动发送数据(单向);
  • 特点:基于 HTTP 协议,无需新协议支持,实现比 WebSocket 简单,适合 “服务器推、客户端收” 的场景。

实现示例

(1)前端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html>
<body>
<div id="dataContainer"></div>
<script>
// 连接SSE端点(后端路径为/events)
const eventSource = new EventSource("/events");

// 连接成功时触发
eventSource.onopen = () => {
console.log("SSE连接已建立");
};

// 收到服务器推送的消息时触发(默认事件类型)
eventSource.onmessage = (event) => {
const container = document.getElementById("dataContainer");
const dataElement = document.createElement("p");
dataElement.textContent = "收到数据:" + event.data;
container.appendChild(dataElement);
};

// 自定义事件类型(如"error"事件)
eventSource.addEventListener("error", (event) => {
console.error("SSE错误:", event.data);
});

// 连接出错时触发(如断开)
eventSource.onerror = () => {
console.log("SSE连接出错,尝试重连...");
// EventSource会自动重连,无需手动处理
};
</script>
</body>
</html>
(2)后端代码(Java Servlet)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

@WebServlet("/events")
public class SSEServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 设置SSE响应头:类型为event-stream,禁用缓存,保持连接
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Connection", "keep-alive");

PrintWriter writer = response.getWriter();
try {
// 维持长连接,每隔2秒推送一次数据
while (true) {
// SSE消息格式:data: 内容\n\n(必须以两个换行符结束)
String data = "data: 实时数据更新,时间:" + new Date() + "\n\n";
writer.write(data);
writer.flush(); // 立即发送(避免缓冲区延迟)

// 模拟数据更新间隔
Thread.sleep(2000);

// 检查连接是否关闭(客户端断开则退出循环)
if (writer.checkError()) {
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writer.close();
}
}
}

SSE 消息格式

SSE 推送的消息需遵循特定格式,否则客户端无法正确解析:

  • 基础格式:data: 消息内容\n\ndata:为前缀,\n\n为结束标志);

  • 自定义事件:event: 事件名\ndata: 消息内容\n\n(客户端通过addEventListener("事件名")监听);

  • 示例:

    1
    2
    3
    4
    5
    6
    // 普通消息
    data: 这是一条普通消息\n\n

    // 自定义事件消息
    event: temperature
    data: 25℃\n\n

优缺点

优点:
  • 实现简单:基于 HTTP 协议,后端无需特殊协议支持,前端仅需EventSource对象;
  • 自动重连EventSource会自动处理连接断开后的重连(默认重试间隔);
  • 轻量级:适合单向推送场景,无需处理双向通信的复杂性。
缺点:
  • 单向通信:仅支持服务器向客户端推送,客户端无法主动发送数据;
  • 数据大小限制:单次推送数据量有限(依赖 HTTP 响应大小限制);
  • 兼容性:IE 浏览器完全不支持(需使用 polyfill 兼容)。

适用场景

  • 单向实时推送场景(如实时新闻通知、股票行情更新、监控告警);
  • 对实现复杂度敏感,无需双向交互的场景;
  • 基于 HTTP 生态,希望复用现有服务器配置(如反向代理、认证)的场景。

三种方案对比与选择指南

维度 轮询 WebSocket SSE
通信方向 单向(客户端请求) 双向(全双工) 单向(服务器推送)
连接类型 短连接(多次 HTTP) 长连接(TCP 复用) 长连接(HTTP 复用)
实时性 低(取决于间隔) 高(毫秒级) 高(毫秒级)
实现复杂度 低(仅 HTTP) 中(需协议支持) 低(基于 HTTP)
兼容性 所有浏览器 现代浏览器(IE10+) 现代浏览器(IE 不支持)
带宽效率 低(无效请求多) 高(无重复握手) 高(无重复握手)
适用场景 低实时性、简单场景 双向交互、高实时性 单向推送、轻量场景

选择建议:

  1. 简单场景,实时性要求低:选轮询(如后台数据每 5 分钟更新一次);
  2. 双向交互,高实时性:选 WebSocket(如即时聊天、在线游戏);
  3. 单向推送,轻量需求:选 SSE(如新闻推送、实时监控数据展示)

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10