当今web应用程序对实时通信的需求越来越高,为了满足客户需求,轮询和长轮询成为常用的技术手段。本文将深入讲解一下轮询实现原理、优缺点和使用场景。
轮询是一种客户端与服务器之间实时通信的技术手段。
基本原理:客户端定期发送请求来查询服务器是否有新数据或事件,并将响应返回给客户端。如果服务器有新的数据或事件,则将其返回给客户端;如果没有,则返回一个空响应。客户端收到响应后,可以处理数据或事件,并根据需要继续发送下一个请求。
定义:轮询是指在一定的时间间隔内,定时向服务器发送请求,获取最新数据的过程。轮询通常用于从服务器获取实时更新的数据。
// 客户端代码
function pollServer() {
fetch('/api/data')
.then(response => response.json())
.then(data => {
// 处理服务器响应的数据
console.log('Received data:', data // 继续下一次轮询
setTimeout(pollServer, 5000);
});
}
// 开始轮询
pollServer();
优点:简单易实现,适用于各种浏览器和服务器。
缺点:轮询会产生大量的无效请求,浪费带宽和服务器资源,产生不必要的网络流量和延迟,并对服务器和客户端资源造成额外的负担。同时,在短时间内频繁地发送请求可能会被服务器视为恶意行为,导致 IP 被封禁等问题
注:轮询适用于不需要实时性的应用场景
长轮询是一种改进的轮询技术,其主要目的是降低轮询过程中的资源消耗和延迟。长轮询的基本原理是客户端发送一个HTTP请求给服务器,并保持连接打开,直到服务器有新的数据或事件时才返回响应给客户端。在这期间,服务器会一直保持连接打开,直到超时或有新数据或事件
// 客户端代码
function longPollServer() {
fetch('/api/data')
.then(response => {
if (response.status === 204) {
// 服务器返回204表示没有新数据或事件,继续进行长轮询
longPollServer();
} else if (response.status === 200) {
// 服务器返回200表示有新数据或事件,处理数据并进行下一次长轮询
response.json().then(data => {
console.log('Received data:', data);
// 继续进行长轮询
longPollServer();
});
}
});
}
//开始长轮询
longPollServer
优点:长轮询相较于轮询技术来说,减少了不必要的网络流量和请求次数,降低了服务器和客户端的资源消耗
缺点:相对于传统的轮询技术,长轮询的实现更加复杂,并且需要服务器支持长时间保持连接的能力。
注:长轮询适用于对实时性要求较高的应用场景,例如在线游戏、即时消息推送等。在这些场景下,降低延迟和减少不必要的资源消耗对于提供良好的用户体验非常重要。
轮询和长轮询都是实现客户端与服务器实时通信的技术手段,它们在资源消耗、延迟和实时性等方面存在差异。
轮询是在固定的时间间隔内向服务器发送请求,即使服务器没有数据更新也会继续发送请求(不需要实时性要求很高的场景)。
长轮询是先发送一个请求,服务器如果没有数据更新,则不会立即返回,而是将请求挂起,直到有数据更新时再返回结果(对实时性要求较高的场景)。
案例1:使用轮询实现实时时间更新的简单示例代码:
// 客户端代码
function pollServer() {
发送HTTP请求给服务器
fetch('/api/time')
.then(response => response.json())
.then(data => {
// 处理服务器响应的时间数据
const currentTime = new Date(data.time);
document.getElementById('time').innerText = currentTime.toLocaleTimeString();
// 继续下一次轮询
setTimeout(pollServer, 5000);
});
}
// 开始轮询
pollServer();
案例2:可视化动态展示数据,有的需要十秒轮询请求一次,有的需要3分钟请求一次,有的需要一天一次(顺便加一个定时,每天几点钟定时请求一次)
<template>
<div class="echart">
</div>
</template>
<script>
export default {
data() {
return {
num_0:0,
num_1:0,
num_2:0,
timer_10: null,//10S短轮询
timer_3: null, //3分钟短轮询
timer_24: null, //24小时长轮询
};
},
destroyed() {
//离开页面是时记得销毁
this.clearAll()
},
created() {
// 进入页面开始请求
this.getShotList3()
this.getShotList10()
this.getLongList()
// 开启定时
this.startRequest()
},
methods: {
startRequest() {
//10S短轮询:组合使用setInterval setTimeout实现轮询可解决浏览器崩溃
this.timer_10 = window.setInterval(() => {
setTimeout(this.getShotList10(), 0);
},10000);
// 3分钟短轮询
this.timer_3 = window.setInterval(() => {
setTimeout(this.getShotList3(), 0);
}, 180000);
// 每日一次的轮询:每日定点7点查询一次;计算当前时间和目标时间的时间间隔,如果超过,则设置定时查询的时间间隔为距离明天目标小时的距离,如果还没到就设置时间为当前时间到目标小时的距离。
this.setRegular(7);
},
setRegular(targetHour) {
let nowTime = new Date()
let nowSeconds = nowTime.getHours() * 3600 + nowTime.getMinutes() * 60 + nowTime.getSeconds() // 计算当前时间的秒数
let targetSeconds = targetHour * 3600 // 计算目标时间对应的秒数
// 判断是否已超过今日目标钟点数,若超过,时间间隔设置为距离明天目标小时的距离
let timeInterval = targetSeconds > nowSeconds ? targetSeconds - nowSeconds: targetSeconds + 24 * 3600 - nowSeconds
this.timer_24 = window.setInterval(() => {
setTimeout(this.getLongList(), 0)
}, timeInterval * 1000);
},
getLongList() {
console.log("24小时轮询" + this.num_0++ + "次");
},
clearAll() {
clearInterval(this.timer_10);
clearInterval(this.timer_3);
clearInterval(this.timer_24);
this.timer_10 = null;
this.timer_3 = null;
this.timer_24 = null;
console.log('销毁');
},
// 10S的短轮询 数据全部拿到之后在进行下一次请求
getShotList10() {
console.log("10秒钟请求" + this.num_1++ + "次");
},
// 3分钟的短轮询 数据全部拿到之后在进行下一次请求
getShotList3() {
console.log("3分钟请求" + this.num_2++ + "次");
},
}
}
</script>
<style scoped>
.echart{
width:100%;
height:100%
}
</style>