COMET探索系列二【Ajax轮询复用模型】

写在前面:Ajax轮询相信大家都信手拈来在用,可是有这么一个问题,如果一个网站中同时有好多个地方需要用到这种轮询呢?就拿我们网站来说,有一个未读消息数提醒、还有一个时实时加载最新说说、昨天又加了一个全网喊话,以后还会要有类似功能添加是肯定的,难道要为每个功能都创建一个独立的轮询?要知道轮询请求中有大半是无用,会对服务器资源和宽带造成巨大的浪费。因此在页面中每增加一个轮询点,对服务器的压力及宽带浪费都将成倍的增长。再考虑一个情况,如果当前网页中需要的不仅是简单的Ajax轮询,而是Ajax长轮询呢?比如在一个 WebIM 应用中同时与两个好友聊天,是不是要为每个好友都建立一个长轮询呢?这不科学,这也不现实,HTTP1.1 协议中规定,同一客户端同一时间最多只能与服务器保持两个连接,问题就变得很棘手了。于是想,所有的轮询无非都是定时向服务器取消息,那么我们是不是只建立一个轮询点定时向服务器取消息,而服务器则将客户端所有需要的消息整理打包后一次性发送给该轮询点,再由该轮询点对返回的消息进行逐点分配处理,心动不如行动,程序员用代码说话。

<!--客户端代码-->

var rtmsg = {

    //请求地址

    uri: U("public/RtMsg/rtmsg"),

    //要获取的消息,可以根据需要动态增减,如从模板变量进行分配,这样可以减少不必要的数据浪费宽带

    items:'UnreadCount,Allnet,newfeed',

    //建立连接

    connect: function () {

        $.ajax({

            url: rtmsg.uri,

            data:{'items':rtmsg.items},

            type: 'post',

            dataType: 'json',

            success: function (res) {

                //请求成功后通过HadnleRes对返回的结果进行分配处理

                rtmsg.HandleRes(res.data);

            }

        })

    },

    //将取回的结果分配给对应的处理程序

    HandleRes: function (res) {

        for (x in res) {

            eval('rtmsg.Handle' + x + '(res[x])');

        }

    },

    //处理未读消息数提醒

    HandleUnreadCount: function (res) {

        /*…………*/

    },

    //处理全网喊话

    HandleAllnet: function (allnet) {

        /*…………*/

    },

    //最新说说处理

    HandleNewfeed: function (newfeed) {

        /*…………*/

    }

    /*我还想要*/

    /*我还可以再加*/

};

$(function () {

    //每隔10秒再来一次

    setInterval(rtmsg.connect, 10000);

    //页面加载完毕先来一次

    rtmsg.connect();

})
<!--服务器端程序,注:我这里使用的是ThinkPHP框架-->
<?php
/**
* 实时消息推送模块
*/
class RtMsgAction extends Action
{
/**
* 模块初始化
*/
function _initialize()
{
//关闭session,连接占用session导至页面等待
session_write_close();
//不限请求超时
set_time_limit(0);
} public function rtmsg()
{
//接收要获取的消息项
$items = $_REQUEST['items'];
//收集消息
$rt = $this->collect(explode(',', $items));
//返回的消息格式大至如下
$rt = array(
'UnreadCount'=>array('status'=>1,'data'=>array('total'=>5,'system'=>2,'message'=>3)),
'Allnet'=>array('status'=>1,'data'=>array('消息一','消息二','消息三','data格式自定,方便客户端处理为好'),
'Newfeed'=>array('status'=>0,'data'=>false)
)
//返回json格式消息
echo json_encode($rt);
} /**
* 收集需要返回给客户端的信息
*/
public function collect($items)
{
//设置未传items时默认获取哪些消息
$items = $items?$items:array('UnreadCount', 'Allnet', 'Newfeed');
$rt = array();
//逐项获取消息
foreach ($items as $v) {
$rt[$v] = call_user_func(array(&$this, 'get' . $v));
} return $rt;
} /**
* 获取用户的通知统计数目
*/
public function getUnreadCount()
{
/*获取消息过程省略,返回信息格式如下*/
$data['status'] = '获取信息状态';
$data['data'] = '信息详情';
return $data;
} /**
* 获取全网喊话
*/
public function getAllnet()
{
/*获取消息过程省略,返回信息格式如下*/
$data['status'] = '获取信息状态';
$data['data'] = '信息详情';
return $data;
} /**
* 获取最新说说
*/
public function getNewfeed()
{
/*获取消息过程省略,返回信息格式如下*/
$data['status'] = '获取信息状态';
$data['data'] = '信息详情';
return $data;
} }

基实本模型的关键在于各消息的分类收集,及返回消息之后的分配处理,即代码中以下两个部分

//客户端:将取回的结果分配给对应的处理程序

HandleRes: function (res) {

    for (x in res) {

        eval('rtmsg.Handle' + x + '(res[x])');

    }

}
//服务器端:逐项收集信息

foreach ($items as $v) {

    $rt[$v] = call_user_func(array(&$this, 'get' . $v));

}

至此Ajax轮询复用模型就建好了,技术没什么技术,关键是巧合,最后总结一下这种模型的优缺点。

优点:避免了客户端多个轮询点造成的服务器资源及宽带浪费,可以动态更改需要获取的消息项,便于管理。

缺点:没有办法为每个需要轮询的点分配独立的请求频率,一般情况下无所谓啦,如果是在长轮询中使用该缺点不复存在。

本文纯属原创,转载请注明出处。

上一篇:ES6的一些常用特性


下一篇:【Javascript】解决Ajax轮询造成的线程阻塞问题(过渡方案)