前两天在写 SSE 文章的时候突然想起来了轮询方案,但是隐约记得轮询也是有区分的,简单回顾一下!
短轮询
原理
- 简单的讲短轮询其实就是 setInterval( ) 方法不断地请求后台,以达到实时更新数据的目的。
- 比较装*的语言就是:客户端按照固定的时间间隔向服务器发送请求,询问是否有新的数据。无论服务器是否返回了新数据,客户端都会在指定的时间间隔后再次发起请求,形成循环查询的机制。
实现方案
前端直接使用 setInterval 方法反复调用接口即可,当然也有其他方案,但是最终目的大同小异,就是不断地请求后台。
代码
// 客户端
const getMessage = function() {
setInterval(() => {
this.$http.get('/queryMessage')
.then(response => {
this.message = response.data.message;
});
}, 5000); // 每5秒请求一次
}
// 服务端
async function queryMessage() {
const { ctx } = this;
// 假设我们有一个方法来获取新消息
const newMessage = await this.getNewMessage();
if (newMessage) {
ctx.body = { message: newMessage };
} else {
ctx.body = { message: 'No new messages' };
}
}
弊端
- 不断请求后台接口容易造成资源浪费。
- 不管有没有新数据都不断请求效率太低。
- 如果后台服务异常,客户端容易不断抛出异常。(这一点我就被客户骂过)
长轮询
原理
长轮询是一种稍微复杂的轮询技术,旨在减少不必要的请求次数。
当客户端发起请求时,如果服务器当前没有新数据,则不是立即返回响应,而是保持这个请求连接打开一段时间,等待新数据的到来。
一旦有新数据或者等待时间超时,服务器就会返回响应给客户端。之后,客户端会立刻发起下一个请求,重复这个过程。
实现方案
- 当客户端发起请求的时候,如果服务器没有新数据,当前请求并不立即返回响应,而是将当前请求暂时挂起,等待新数据。
- 如果在等待一段时间以后存在数据更新,则立即将数据返回。(这里需要注意客户端接口请求超时时间)
- 如果在等待一段时间以后不存在数据更新,则将空数据返回。
- 当客户端获得响应后,立即发起下一次请求。
代码
// 客户端
const getMessage = async function () {
try {
const response = await this.$http.get('/queryMessage');
this.message = response.data.message;
} catch (error) {
console.error("Error during polling", error);
}
getMessage();
}
// 服务端
async function queryMessage() {
const { ctx } = this;
let newMessage = null;
const timeout = 30000; // 设置超时时间
const startTime = Date.now();
// 循环等待新消息或超时
while (!newMessage && Date.now() - startTime < timeout) {
newMessage = await this.getNewMessage();
if (!newMessage) {
await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒
}
}
ctx.body = { message: newMessage || 'No new messages within timeout' };
}
弊端
- 尽管长轮询减少了客户端频繁发起请求的次数,但每次请求都会占用服务器的一个连接,直到有新数据或超时。在高并发场景下,这可能会导致服务器资源紧张。
- 在移动网络环境下,由于网络状态不稳定,长轮询可能导致更多的连接重试,消耗更多电量,并且影响用户体验。
总结
其实无论长轮询或者短轮询只是一种选择,最重要的是选择合适的方案。(最重要的是不要让客户看到报错!!!)
示意图