数据实时:轮训技术方案有哪些
数据实时性
应该有好多产品会说咱们数据需要
实时性
,就是要让界面跟数据保存紧密的链接,那么提到实时
,大家脑袋想到的方案有那些呢?setInterval
,WebSocket
?
实现轮询数据主要是以下几种技术方案:
-
定时器轮询:在客户端使用
JavaScript
设置一个定时器,每隔一段时间就向服务器发送请求。这种方式简单易用,但会导致大量的无效请求,同时也可能因为间隔时间不合适而导致数据延迟。 -
长轮询(Long Polling):在客户端向服务器发送请求后,服务器将保持连接打开,直到有新数据可用或超时。当有新数据可用时,服务器立即返回响应并关闭连接。如果超时则重新建立连接并重试。这种方式可以减少无效请求和延迟,但需要服务器支持。
-
WebSockets
:WebSocket
是一种双向通信协议,允许服务器向客户端实时推送数据。使用WebSocket
可以有效地降低网络延迟和资源浪费,但需要对Web
服务器和客户端进行额外配置。 -
SSE(Server-Sent Events):SSE 是一种服务器向客户端实时推送数据的技术,与 WebSockets 类似,但更加轻量级。相比 WebSocket,SSE 更容易实现和部署,但功能相对较弱。
第一种-定时器轮询
定时器轮询就是再
js
写一个setInterval
,比如产品需要表格数据需要15s
同步下数据库数据,那么咱们使用setInterval
也是最便捷最合适的方案。但是使用定时器需要注意销毁时机!!如下一个更新时间的定时器的使用:
// index.js
import * as dayjs from 'dayjs';
const WEEKLY_MAP = {
1: '星期一',
2: '星期二',
3: '星期三',
4: '星期四',
5: '星期五',
6: '星期六',
7: '星期天',
};
export const getWeekly = () => WEEKLY_MAP[new Date().getDay()];
export const getDate = () => dayjs().format('YYYY.MM.DD');
export const getTime = () => dayjs().format('HH:mm:ss');
<template>
<div>
<label class="hour">{{ currentTime }}</label>
<label class="day">{{ currentDate }}</label>
</div>
</template>
<script>
import { getTime, getDate } from '@/utils/index';
export default {
name: 'Time',
data () {
return {
currentTime: getTime(),
currentDate: getDate(),
timer: null,
}
},
mounted () {
this.getRealTime();
},
beforeDestroy () {
clearInterval(this.timer); // 使用定时器,不要忘记清除定时器
},
methods: {
getRealTime () {
this.timer = setInterval(() => {
this.currentTime = getTime();
}, 1000);
},
},
}
</script>
这种方式适合长时间,固定时间轮询一次的场景。像大屏、仪表盘等场景
第二种 长轮询(Long Polling)
是一种通过定时向服务器发送请求,并在服务器上保持连接打开,直到有新数据可用或超时后返回响应的方式。
与定时器轮询不同的是,长轮询在客户端向服务器发送请求后,服务器并不立即返回响应,而是将连接保持打开,等待数据可用或超时。当有新数据可用时,服务器会立即返回响应并关闭连接。如果超时,则重新建立连接并重试。
长轮询可以减少无效请求和延迟,并且可以实现双向通信,即服务器可以向客户端主动推送数据。但是,长轮询需要服务器支持并维护大量的连接,因此可能会对服务器性能造成影响。
长轮询的工作原理如下:
- 客户端向服务器发起请求。
- 服务器接收到请求后,进行处理,如果没有新数据可用,则将连接保持打开。
- 当有新数据可用时,服务器将立即返回响应并关闭连接。
- 如果超时,则重新建立连接并重试。
在实现长轮询时,需要考虑以下几点:
- 服务器需要支持长轮询,即能够保持连接打开并处理请求。
- 长轮询会占用服务器资源,需要合理设置连接超时时间和最大连接数,以避免服务器负载过高。
- 客户端需要在收到响应后立即重新建立连接,否则会导致数据延迟。
- 长轮询需要一些额外的开销和复杂性,因此可以考虑使用 WebSocket 或 SSE 等更为现代化的技术来实现长连接。
// getDataApi 是封装的函数 就是用axios封装阿api交互函数
const LONG_POLLING_STATUS = {
PADDING: 0,
FINISH: 1,
}
async function longPolling() {
const response = await getDataApi();
if(response?.data === LONG_POLLING_STATUS.PADDING) {
longPolling(); // 重新发起请求
}
if(response?.data === LONG_POLLING_STATUS.FINISH) {
// 正常获取到数据的处理逻辑
longPolling();// 发起下一轮的请求
}
}
说的简单点就是接口一直
padding
,一直到有结果返回或者响应超时给个返回值这个时候前端再重新发起一下这个请求。 跟定时器相比优点是少了一些网络请求。
第三种-WebSockets
WebSockets
提起这个大家应该比较熟悉了是一种双向通信协议,像一些平台的聊天软件就是使用的这个技术,它有以下方面优越点:
优点:
- 实时性好:WebSocket 长连接可以实现实时数据传输和响应,避免了频繁的请求和响应。
- 低延迟、高性能:WebSocket 可以降低网络延迟,并减少服务器资源开销,因为它使用单一的 TCP 连接,而不是每次请求都需要建立新的连接。
- 跨平台、跨浏览器:WebSocket 协议是 Web 标准之一,可以在各种平台和浏览器上使用。
- 易于实现:相对于其他实时通信技术(如 Comet),WebSocket 更加易于实现和维护,可直接使用标准化的 API。
缺点:
- 兼容性问题:旧版浏览器可能不支持 WebSocket,需要进行兼容性处理。
- 安全性问题:由于 WebSocket 使用非标准端口,可能会被拦截或攻击。为了保证安全,需要使用 SSL/TLS 加密协议来加密 WebSocket 连接。
- 维护成本高:WebSocket 长连接需要占用服务器资源,需要设置合理的超时时间和最大连接数,否则会增加服务器的负荷。
如下是使用node建立一个WebSocket
示例,以及使用示例:
// node
// 引入 ws 和 express 模块
const WebSocket = require('ws');
const express = require('express');
// 实例化 express 应用程序
const app = express();
// 静态资源路径
app.use(express.static(__dirname + '/public'));
// 创建 WebSocket 服务器
const server = new WebSocket.Server({ port: 3000 });
// WebSocket 连接处理
server.on('connection', function connection(ws) {
console.log('客户端已连接');
// 接收客户端消息
ws.on('message', function incoming(message) {
console.log('接收到消息: %s', message);
// 将消息发送给所有客户端
server.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
// 断开连接处理
ws.on('close', function close() {
console.log('客户端已断开连接');
});
});
// 启动 Web 服务器
app.listen(8080, () => {
console.log('Web 服务器已启动:http://localhost:8080');
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket 测试</title>
<script>
var ws = new WebSocket('ws://localhost:3000');
// 当连接建立时
ws.onopen = () => {
console.log('已连接');
};
// 当收到消息时
ws.onmessage = (event) => {
console.log('收到消息:' + event.data);
document.getElementById('output').textContent += '\n' + event.data;
};
// 当连接关闭时
ws.onclose = () => {
console.log('已断开连接');
};
// 发送消息
function sendMessage() {
var message = document.getElementById('input').value;
ws.send(message);
}
</script>
</head>
<body>
<h1>WebSocket 测试</h1>
<div>
<textarea id="output" rows="10" cols="50"></textarea>
</div>
<div>
<input type="text" id="input" placeholder="输入消息">
<button onclick="sendMessage"></button>
</div>
</body>
</html>
实现 WebSocket 并不难,而且现在已经有很多成熟的库和框架可以用来快速地完成 WebSocket 的开发。一些主流编程语言都提供了 WebSocket 相关的库和框架,如 Python 中的 Tornado、Java 中的 Netty、Node.js 中的 Socket.io、ws 等等。同时也有一些第三方的库或框架,例如 C# 中的 SignalR。
第四种- SSE(Server-Sent Events)
SSE(Server-Sent Events)
是一种通过普通的HTTP
协议实现服务器向客户端实时推送数据的技术。SSE
与WebSocket
技术类似,也能实现双向通信,但是相比之下更加轻量级。
SSE vs. WebSocket
与 WebSocket
不同的是,SSE
基于 HTTP/1.1
协议,因此与标准的 Web 技术(如 HTTP 缓存和代理)兼容性较好,不需要进行特殊配置就可以使用。SSE
的工作原理是:客户端通过 HTTP 连接服务器,并发送一个请求来建立 SSE
连接。服务器收到请求后,将保持连接打开,随时可以向客户端发送数据。如果没有数据需要发送,服务器会发出一条空消息以防止连接超时或关闭。
SSE
是单向通道,只能服务器向客户端发送消息,如果客户端需要向服务器发送消息,则需要一个新的 HTTP 请求。 这对比 WebSocket
的双工通道来说,会有更大的开销。这么一来的话就会存在一个「什么时候才需要关心这个差异?」的问题,如果平均每秒会向服务器发送一次消息的话,那应该选择 WebSocket
。如果一分钟仅 5 - 6 次的话,其实这个差异并不大。
SSE示例如下:
var http = require("http");
http.createServer(function (req, res) {
var fileName = "." + req.url;
if (fileName === "./stream") {
res.writeHead(200, {
"Content-Type":"text/event-stream",
"Cache-Control":"no-cache",
"Connection":"keep-alive",
"Access-Control-Allow-Origin": '*',
});
res.write("retry: 10000\n");
res.write("event: connecttime\n");
res.write("data: " + (new Date()) + "\n\n");
res.write("data: " + (new Date()) + "\n\n");
interval = setInterval(function () {
res.write("data: " + (new Date()) + "\n\n");
}, 1000);
req.connection.addListener("close", function () {
clearInterval(interval);
}, false);
}
}).listen(8844, "127.0.0.1");
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="example"></div>
<script>
var source = new EventSource('http://127.0.0.1:8844/stream');
var div = document.getElementById('example');
source.onopen = function (event) {
div.innerHTML += '<p>Connection open ...</p>';
};
source.onerror = function (event) {
div.innerHTML += '<p>Connection close.</p>';
};
source.addEventListener('connecttime', function (event) {
div.innerHTML += ('<p>Start time: ' + event.data + '</p>');
}, false);
source.onmessage = function (event) {
div.innerHTML += ('<p>Ping: ' + event.data + '</p>');
};
</script>
</body>
</html>
如下图:
ChatGPT
的打字机效果数据传输就是使用的这个SSE(Server-Sent Events)
,SSE
主要用于实现实时通知和事件更新等功能。在网页中,SSE
技术常被用于无需刷新页面实时更新新闻、股票行情和聊天信息等应用场景。
总结
以上就是数据实时轮询方式的方案了,在选择轮询数据的方式时,需要考虑场景和需求,并根据实际情况选择最适合的方式。