改进长轮询Ajax性能

5

我正在编写一个Web应用程序(仅兼容Firefox),它使用长轮询(通过jQuery的ajax能力)从服务器向客户端发送更多或更少的常量更新。我担心让它长时间运行,比如一整天或过夜,会产生什么影响。基本的代码框架如下:

function processResults(xml)
{
    // do stuff with the xml from the server
}

function fetch()
{
    setTimeout(function ()
    {
        $.ajax({
            type: 'GET',
            url: 'foo/bar/baz',
            dataType: 'xml',
            success: function (xml)
            {
                processResults(xml);
                fetch();
            },
            error: function (xhr, type, exception)
            {
                if (xhr.status === 0)
                {
                console.log('XMLHttpRequest cancelled');
                }
                else
                {
                    console.debug(xhr);
                    fetch();
                }
            }
        });
    }, 500);
}

(半秒钟的“休眠”是为了防止客户端在更新返回到客户端时过快地对服务器进行攻击-通常情况下会这样。)

让它整晚运行后,Firefox 通常会变得很慢。我一直认为这可能部分原因是堆栈深度太大,因为我基本上编写了一个无限递归函数。然而,如果我使用 Firebug 并在 fetch 中设置断点,看起来情况并非如此。即使经过一个小时,Firebug 显示给我的堆栈只有4或5帧。

我正在考虑的解决方案之一是将我的递归函数更改为迭代函数,但我无法弄清楚如何在 Ajax 请求之间插入延迟,而又不会出现“旋转”现象。���已经查看了 JS 1.7 “yield” 关键字,但我无法完全理解它,以确定它是否适用于此处。

最好的解决方案是定期对页面进行强制刷新,例如每小时一次吗?是否有更好/更简洁的长轮询设计模式,即使运行8或12小时也不会对浏览器造成伤害?还是我应该跳过长轮询,改用其他“常量更新”模式,因为我通常知道服务器将以多频繁的方式向我响应?

4个回答

2

也有可能是FireBug的原因。您正在使用console.log记录信息,这意味着您可能已经打开了网络监视器选项卡等,这意味着每个请求都存储在内存中。

尝试禁用它,看看是否有帮助。


2

我怀疑processResults()存在内存泄漏问题。

我在一个长轮询的Web应用程序中使用了与您非常相似的代码,该应用程序能够在数周内不间断运行而无需刷新页面。

您的堆栈不应该很深,因为fetch()会立即返回。 您没有无限递归循环。

您可能需要使用Firefox的Leak Monitor Add-on来帮助您查找内存泄漏问题。


我认为这是另一种“打破”栈深度的方法,但我并没有看到其与原始代码中的 setTimeout 产生任何不同的效果。 - Matt Ball
是的,但是你的堆栈不应该变得非常深,因为fetch()会立即返回,因此成功和失败函数会相当快地退出堆栈。 - Daniel Vassallo
1
可以的。在FF中检测内存泄漏的最佳工具是什么? - Matt Ball
1
我刚意识到我已经很长时间没有禁用Firebug运行它了。这很可能是问题所在。 - Matt Ball
1
@Bears:这可能是原因,但是我仍然能够使用Firebug打开我的长轮询应用程序并保持数天而不需要刷新页面,也没有明显的内存泄漏。然而,Firebug可能会因为processResults()中的某些合法操作而导致内存泄漏,而我自己的应用程序并没有这样做。所以是的,它可能是你的问题的原因。 - Daniel Vassallo
显示剩余2条评论

1
"不建议从方法内部调用fetch()。当您预计在某个点上该方法将结束并开始向调用者发送结果时,递归更好地使用。问题是,当您递归调用方法时,它会保持调用者方法开启并使用内存。如果您只有3-4帧深度,那是因为jQuery或浏览器以某种方式“修复”了您的错误。

最近的jquery版本默认支持长轮询。这样,您就可以确保不依赖于浏览器的智能来处理您的无限递归调用。在调用$.ajax()方法时,您可以使用以下代码组合进行长轮询和500毫秒的安全等待再进行新的调用。"

function myLongPoll(){
    setTimeout(function(){
        $.ajax({
            type:'POST',
            dataType: 'JSON',
            url: 'http://my.domain.com/action',
            data: {},
            cache: false,
            success:function(data){

                //do something with the result

            },
            complete: myLongPoll, 
            async : false,
            timeout: 5000
        });
   //Doesn't matter how long it took the ajax call, 1 milisec or 
   //5 seconds (timeout), the next call will only happen after 2 seconds
   }, 2000);

这样你就可以确保在下一个调用之前关闭$.ajax(),可以通过在$.ajax()调用之前和之后添加简单的console.log()来证明。

使用jQuery的complete回调与我的问题中的实现没有显着区别。这不是Java - 调用者永远不需要显式关闭XHR或其他资源。 - Matt Ball

1

4-5的堆栈深度是正确的。setTimeout$.ajax是异步调用,它们会立即返回。回调函数稍后由浏览器在空调用堆栈中调用。由于无法以同步方式实现长轮询,因此必须使用这种递归方法。没有办法使其迭代。

我怀疑减速的原因是您的代码存在内存泄漏。泄漏可能在jQuery的$.ajax中(非常不可能)或者在您的processResults调用中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接