web请求中的 轮询与长轮询

之前偶然看到这个 于是就查了一下 发现网上的资料博客基本都是在扯淡。。。。

首先总结一下网上博客那些错误

  1. 两者的区别是在实现的逻辑上 特别是服务端的实现方式相关 与用iframe还是ajax实现并没有关系
  2. 长轮询和长连接并没什么关系 长连接是传输层的一种实现 长轮询是应用层的一种实现
    一般来说 相同的功能 web的可以使用反向ajax 服务器直接向浏览器发送数据实现 但是既然用轮询 那就研究一下吧 自己弄懂了之后 写了一个例子出来 我前端用js 后端用nodejs 框架用的express 其他平台差不多 客户端和服务端各有两种实现方式

客户端(浏览器)

0. 使用 setInterval 无脑轮询请求

1
2
3
4
5
function pollingCallback (e) {
log(e)
};
//进行轮询,间隔时间为3s,
setInterval(post,3000,url_polling,data,pollingCallback);

1. 递归请求

使用递归请求在回调函数里面发起新的请求

1
2
3
4
5
6
7
function pollingCallback (e) {
log(e);
//请求完成后进行下一次请求
post(url_polling, data, pollingCallback);
};
//开始请求数据
post(url_polling, data, pollingCallback);

这个两个的优缺点大家也都看的出来 两个方法需要配合服务器以及开发需求选择合适的就可以了

2. iframe

很多查到的资料还有使用iframe来实现轮询的 完全可以模拟方法0和1 post也用form也可以实现 而且还有个很大的好处:请求可以中断 但是我还没见用这种方法的实现 本篇不会过多讲这中方式

可以用 iframe 的reload函数进行刷新 以下代码没有测试 估计运行不了

1
2
3
4
5
6
7
8
9
10
11
12
//模拟 方法0  一旦有请求延迟 后面的再次请求的时候 前面的请求会自动取消
iframe.onload = function(){
pollingCallback(iframe.contentDocument);
}
setInterval(fuction(){iframe.src=url_polling;},2000);

//模拟 方法1
iframe.onload = function(){
pollingCallback(iframe.contentDocument.documentElement.innerHTML);
iframe.src=url_polling;
}
iframe.src=url_polling;

服务端(nodejs)

说明:getRespTpl() 返回一个模板对象 , checkNewData(callback) 用来检查是否有新数据 并有一个回调作为参数

0. 普通实现:每次请求都去查询新数据 并返回结果

1
2
3
4
5
6
7
8
9
10
exports.polling = function (req, resp) {
var pollingCallback = function (newData) {
var data = getRespTpl(req);
if (newData) {
data['data'] = newData;
};
resp.send(JSON.stringify(data));
}
checkNewData(pollingCallback);
}

1.使用长轮询实现:每次请求数据时先hold住连接不着急返回数据 等有新的数据后再返回新数据

当然客户端在这里有个超时时间 为了避免被超时 服务器需要每隔一段时间返回数据 为了方便起见这个下面的例子不包含超时逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
exports.longpolling = function (req, resp) {
var longpollingCallback = function (newData) {
var data = getRespTpl(req);
if (newData) {
data['data'] = newData;
resp.send(JSON.stringify(data));
}else{
//每次检查完后隔一段时间后进行第二次查询
setTimeout(checkNewData,3000,longpollingCallback);
//当然也可以马上进行查询 但是这样服务器压力可能会增大
//checkNewData(longpollingCallback);
}
}
checkNewData(longpollingCallback);
}

好了 现在可以两两组合看看了 2 × 2 就是四种方式了

组合 0-0:普通轮询

大部分轮询都是这样的组合 简单 容易实现 但是大部分网络请求都是无效的 浪费网络资源 而且有一个请求出问题了不影响后面的请求 但是一个请求延迟了后面的请求仍然会发送 无法保证数据的顺序

组合 1-0:优化了的普通轮询

这个是组合0-0的优化方式 同样大部分请求是无效的 但是能保证数据的顺序和请求的顺序是一致的 一旦请求出问题还可以重新获取这个请求 这个请求如果是

组合0-1:

这种组合你特么不是在逗我吗? 完全没有意义

组合 1-1 : 这就是长轮询!!!(尼玛终于讲到重点了)

这种方式很好的解决了前面几种的缺点 缺点主要是服务端代码要特殊处理 而且每次hold住请求一旦请求多了同时hold较多连接的时候 会很耗服务器资源

示例代码在这里 :传送门 只有 组合0-0 与组合 1-1 的实现 且对服务端实现1进行了超时处理