长轮询冻结浏览器并阻止其他 ajax 请求
Posted
技术标签:
【中文标题】长轮询冻结浏览器并阻止其他 ajax 请求【英文标题】:Long polling freezes browser and block other ajax request 【发布时间】:2012-09-28 09:31:56 【问题描述】:我正在尝试在我的 Spring-MVC Web App 中实现长轮询,但它会在 4-5 次继续 AJAX 请求后冻结我的浏览器和其他请求。我不知道这里发生了什么是我的相关代码。
控制器方法:(服务器端):-
@Asynchronous
@RequestMapping("/notify")
public @ResponseBody
Events notifyEvent(HttpServletRequest request)
Events events = null;
try
events = (Events) request.getSession(false).getServletContext().getAttribute("events");
System.out.println("Request Came from" + ((com.hcdc.coedp.safe.domain.User) request.getSession(false).getAttribute(Constants.KEY_LOGGED_IN_USER)).getLoginId());
if (!events.getTypeOfEvents().isEmpty())
System.out.println("Removing older entries");
events.getTypeOfEvents().clear();
while (!events.isHappend())
//Waiting for event to happen.
events = Events.getInstance();
events.setHappend(false);
request.getSession(false).getServletContext().setAttribute("events", events);
catch (Exception e)
e.printStackTrace();
return events;
长轮询脚本(客户端):-
$(document).ready(function()
$.ajaxSetup(
async:true//set a global ajax requests as asynchronus
);
alert('Handler for .onload() called.');
waitForMsg();
);
function waitForMsg()
xhr= $.ajax(
type: "POST",
url: '<%=request.getContextPath()%>/notification/notify',
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
global:false,
success: function(data) /* called when request to notifier completes */
/* Doing smthing with response **/
setTimeout(
waitForMsg, /* Request next message */
1000 /* ..after 1 seconds */
);
,
error: function(XMLHttpRequest, textStatus, errorThrown)
addmsg("error", textStatus + " (" + errorThrown + ")");
setTimeout(
waitForMsg, /* Try again after.. */
15000); /* milliseconds (15seconds) */
);
;
更新:
function updateFeed(event, data)
var f=eval(data);
alert(f.typeOfEvents.length);
function catchAll(event, data, type)
console.log(data);
alert("error");
console.log(type);
$.comet.connect('<%=request.getContextPath()%>/notification/notify');
$(document).bind('feed.comet', updateFeed);
$(document).bind('.comet', catchAll);
没有弹出警告框..:(
【问题讨论】:
我认为您使用的是 setTimeout 而不是 setInterval。检查this 我认为冻结它不是问题是完美的,因为我只想一次又一次地调用方法(成功或错误)。 你在你的萤火虫中看到任何问题/错误。如果来自服务器的数据很大,它可能会冻结浏览器。 萤火虫没有错误。数据也只有字符串内容。 有多少个同时连接?某些浏览器限制为 2 个/网页。 【参考方案1】:我遇到了类似的问题,我的浏览器被 AJAX 请求卡住了。提示:直接使用 waitForMsg(),试试 setTimeout("waitForMsg()",10)。
【讨论】:
试过不工作的浏览器仍然显示加载符号和冻结。【参考方案2】:仅供参考,这里有一个项目可能会对您有所帮助:https://github.com/SeanOC/jquery.comet
一般来说,我会搜索可以支持 Web 套接字的 javascript comet API(如果在客户端/服务器上可用,并且可以优雅地回退到长轮询)。 API 应处理所有血腥细节,让您专注于应用程序。
这是关于该主题的旧道场文章的链接:http://dojotoolkit.org/features/1.6/dojo-websocket
祝你好运。
【讨论】:
在您的第一个链接中尝试了解决方案但无法正常工作...更新添加了问题。【参考方案3】:您的浏览器代码中似乎有一个空的 while 循环。这是一种等待事件的 CPU 密集型方式。
如果没有事件发生,客户端将在您期望的 50 秒超时后终止请求。但我不确定服务器线程是否也被杀死,或者它是否永远“同时”运行(除非有事件)。下一个请求将启动第二个服务器线程,该线程也挂在 while 循环中。也许空的while循环的数量对服务器来说是一种过度杀伤,因此它停止接受更多的请求。所以在一些请求之后(每个请求都触发了一个无限的服务器线程),客户端永远等待一个新的请求......因为它不能被服务器处理。
ps:成功后您评论说等待 1 秒,但将超时设置为 10000(10 秒)
【讨论】:
明白你的意思,如果你的代码为我提供了一些代码来有效地进行长轮询,我该如何解决这个问题。谢谢 只是为了检查这是否是原因在while循环中添加以下行:Thread.currentThread().sleep(1000);
这将使当前线程进入睡眠状态,让其他线程有机会运行。调整睡眠时间到所需的反应时间..
这行不通,因为 thred 行为不在我们的控制范围内,所以它会向每个客户发送不同的响应,我们甚至无法确保每个客户都收到相同的响应。我试过了之前。
您的问题和目前的问题是,为什么您的浏览器会死机.. 问题是服务器过载是否是导致此问题的原因。此更改是否仍会导致浏览器冻结?
@DanglingPiyush 它仍然冻结吗?【参考方案4】:
可能是这样的:
xhr= $.ajax( (...)
在您的 waitForMsg 函数中。
试试
var xhr = (...)
可能是您在全局对象中声明了 xhr,从而无法响应两个不同的请求。
【讨论】:
【参考方案5】:您可以尝试使用 jQuery deferred 重写该行为:
function setShortTimeout()
setTimeout(waitForMsg, 1000);
function setLongTimeout()
setTimeout(waitForMsg, 15000);
$(document).ready(function()
$.ajaxSetup(
async:true//set a global ajax requests as asynchronus
);
alert('Handler for .onload() called.');
$.when(waitForMsg())
.done(successHandler, setShortTimeout)
.fail(errorHandler, setLongTimeout);
);
function waitForMsg()
return $.ajax(
type: "POST",
url: '<%=request.getContextPath()%>/notification/notify',
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
timeout:50000, /* Timeout in ms */
global:false
);
;
errorHandler
和 successHandler
将是你的成功:和错误:回调,为了清楚起见,我省略了它们的 setTimeout 部分(因为它现在是 deferred.done() 和 .fail() 回调的一部分)。
让我知道它是否有效。
【讨论】:
【参考方案6】:我是一名 php 开发人员,但我遇到了您的问题,并且可能是相同的行为。所以我给你我的 2 美分,希望对你有帮助。
确实让我怀疑有问题的行是:
events = (Events) request.getSession(false).getServletContext().getAttribute("events");
在 PHP 中,会话存储在文件中,如果我们在会话打开时对 php 脚本进行长轮询,则会遇到race condition 问题。
原理很简单:
-
当请求打开会话时,文件被锁定直到会话
已关闭。
如果有其他请求到达服务器,它们将被锁定,直到
会话从上一个请求中释放。
在长轮询的情况下,如果会话在获取信息后(至少在等待事件之前)打开而不是关闭,所有请求都被锁定,如果您正在其他页面上使用会话。即使您打开一个新标签页,因为对于一个浏览器只有一个会话,您也会被锁定。
【讨论】:
我这样做是因为我想向每个用户浏览器反映相同的事件通知,所以我认为最好的范围是上下文。但是我会尝试一下您的建议。但是 php 有 session_write_close 我不认为 java 有这样的东西。【参考方案7】:好像你遇到了会话文件锁定
对于 PHP
当你不需要会话值时使用session_write_close()
【讨论】:
不是真正的操作要求,但赞成,因为这正是我正在寻找的以上是关于长轮询冻结浏览器并阻止其他 ajax 请求的主要内容,如果未能解决你的问题,请参考以下文章
服务端向客户端推送消息:轮询,长轮询(兼容性好),以及websocket(主流浏览器都支持)