轮询是指客户端定期向服务器查询是否有新的数据更新,是一种常见的实现客户端与服务器数据同步的技术。主要有以下几种轮询方式:
- 短轮询(Short Polling)
这是最简单的一种轮询方式。客户端通过setInterval或类似的定时器每隔一段时间向服务器发送一次请求,服务器立即返回响应数据(无论数据是否有更新)。这种方式实现简单,但缺点是无论服务器数据是否更新,请求都会被发送,导致服务器压力较大,资源浪费。
function poll() {
const xhr = new XMLHttpRequest();
xhr.open('GET', '/data', true);
xhr.onload = function() {
if (xhr.status === 200) {
// 处理响应数据
handleData(xhr.responseText);
// 继续轮询
setTimeout(poll, 5000); // 5秒后继续轮询
}
}
xhr.send();
}
poll(); // 启动轮询
短轮询适用于对实时性要求不太高,数据更新频率较低的场景,比如定期获取天气预报、股票行情等。
- 长轮询(Long Polling)
长轮询技术可以减少不必要的请求,降低服务器压力。客户端发送Ajax请求到服务器后,服务器将请求挂起,直到有新的数据更新才返回响应,客户端收到响应后再次发送新的请求。
这种方式的关键是服务器端要保持请求状态,一旦有新数据就触发响应。连接无法一直保持,需要重新建立新连接。
const http = require('http');
const pendingRequests = [];
const server = http.createServer((req, res) => {
if (req.url === '/subscribe' && req.method === 'GET') {
// 将响应挂起
pendingRequests.push(res);
// 监听客户端断开连接
req.on('close', () => {
pendingRequests.splice(pendingRequests.indexOf(res), 1);
});
} else {
res.statusCode = 404;
res.end('Not found');
}
});
// 每隔一段时间向所有挂起的响应推送数据
setInterval(() => {
const message = { time: new Date().toLocaleTimeString() };
pendingRequests.forEach((res) => {
res.write(`data: ${JSON.stringify(message)}\n\n`);
});
}, 5000);
server.listen(3000, () => console.log('Server is running on http://localhost:3000'));
长轮询适用于偶尔的服务器数据实时更新推送,比如网页操作的实时更新、在线客服实时消息等。
- WebSocket
WebSocket是一种全双工的通信协议,可以在单个TCP连接上进行全双工通信,实现服务器主动推送数据给客户端,也允许客户端随时向服务器发送数据。
WebSocket连接建立后,会以两条单向通道的形式工作,服务端和客户端可以随时主动发送数据给对方,全程只建立一次TCP连接,无需像HTTP每次发送请求都建立新连接。
const socket = new WebSocket('ws://example.com/data');
socket.onopen = function() {
console.log('WebSocket连接已建立');
};
socket.onmessage = function(event) {
handleData(event.data);
};
socket.onerror = function(error) {
console.error('WebSocket连接发生错误:', error);
};
WebSocket非常适合需要双向通信的实时场景,如即时通讯、协同编辑、游戏、金融实时报价等,可以最大程度节约服务器资源和带宽。
- Server-Sent Events(SSE)
SSE(Server-Sent Events,服务器发送事件)是一种基于HTTP的服务器端向客户端推送数据的技术,OpenAI使用SSE实现了流式传输效果。相比传统的轮询方式,它有以下优点:
-
服务器推送 传统轮询需要客户端定时向服务器发起请求,而SSE是服务器主动向客户端推送数据,减少了客户端不必要的请求,节省了带宽和服务器资源。
-
单向通信 SSE是单向通信,即只能由服务器向客户端推送数据,无需客户端发起请求。这种单向通信模型更加高效。
-
实时性 服务器可以实时地将数据推送给客户端,而不需要等待客户端的轮询。这对于需要实时更新数据的应用非常有用,如在线聊天、实时报价等。
-
自动重连 如果连接断开,浏览器会自动重新连接到服务器,确保数据流的连续性。
-
简单 SSE使用HTTP协议,易于开发和部署,且兼容大多数现代浏览器。
SSE的应用场景包括:在线聊天、股票行情更新、社交网络更新、用户在线状态更新、服务器监控等需要服务器实时向多个客户端推送数据的场景。
以下是一个简单的Node.js服务器使用SSE推送数据的示例:
const http = require('http');
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const sendEvent = (data) => res.write(`data: ${JSON.stringify(data)}\n\n`);
sendEvent({ message: 'Hello from Server-Sent Events!' });
const intervalId = setInterval(() => sendEvent({ time: new Date().toLocaleTimeString() }), 1000);
req.on('close', () => {
console.log('Client disconnected');
clearInterval(intervalId);
});
});
server.listen(3000, () => console.log('Server listening on port 3000'));
在浏览器中,可以使用EventSource对象订阅服务器推送的事件:
const eventSource = new EventSource('http://localhost:3000');
eventSource.onmessage = (event) => {
console.log('Received event:', event.data);
};