Servlet 3.0:无法发送异步响应?
我在为用户建立 AsyncContexts 并使用它们向他们推送通知时遇到问题。在页面加载时,我有一些 jQuery 代码来发送请求:
$.post("TestServlet",{
action: "registerAsynchronousContext"
},function(data, textStatus, jqXHR){
alert("Server received async request"); //Placed here for debugging
}, "json");
在“TestServlet”中,我在 doPost 方法中有此代码:
HttpSession userSession = request.getSession();
String userIDString = userSession.getAttribute("id").toString();
String paramAction = request.getParameter("action");
if(paramAction.equals("registerAsynchronousContext"))
{
AsyncContext userAsyncContext = request.startAsync();
HashMap<String, AsyncContext> userAsynchronousContextHashMap = (HashMap<String, AsyncContext>)getServletContext().getAttribute("userAsynchronousContextHashMap");
userAsynchronousContextHashMap.put(userIDString, userAsyncContext);
getServletContext().setAttribute("userAsynchronousContextHashMap", userAsynchronousContextHashMap);
System.out.println("Put asynchronous request in global map");
}
//userAsynchronousContextHashMap is created by a ContextListener on the start of the web-app
但是,根据 Opera Dragonfly(类似 Firebug 的调试工具),服务器似乎发送了 HTTP 500请求发送后约30000ms响应。
浏览器不会收到使用 userAsyncContext.getResponse().getWriter().print(SOME_JSON) 创建并在 HTTP 500 响应之前发送的任何响应,我不知道为什么。仅当处理 AsyncContext 的“if”语句中的所有代码都不存在时,浏览器才会接收到使用常规响应对象发送响应 (response.print(SOME_JSON))。
有人可以帮我吗?我有一种感觉,这是由于我对异步 API 工作原理的误解。我认为我能够将这些 AsyncContext 存储在全局映射中,然后检索它们并使用它们的响应对象将内容推送到客户端。但是,AsyncContext 似乎无法写回客户端。
任何帮助将不胜感激。
I'm having trouble establishing AsyncContexts for users and using them to push notifications to them. On page load I have some jQuery code to send the request:
$.post("TestServlet",{
action: "registerAsynchronousContext"
},function(data, textStatus, jqXHR){
alert("Server received async request"); //Placed here for debugging
}, "json");
And in "TestServlet" I have this code in the doPost method:
HttpSession userSession = request.getSession();
String userIDString = userSession.getAttribute("id").toString();
String paramAction = request.getParameter("action");
if(paramAction.equals("registerAsynchronousContext"))
{
AsyncContext userAsyncContext = request.startAsync();
HashMap<String, AsyncContext> userAsynchronousContextHashMap = (HashMap<String, AsyncContext>)getServletContext().getAttribute("userAsynchronousContextHashMap");
userAsynchronousContextHashMap.put(userIDString, userAsyncContext);
getServletContext().setAttribute("userAsynchronousContextHashMap", userAsynchronousContextHashMap);
System.out.println("Put asynchronous request in global map");
}
//userAsynchronousContextHashMap is created by a ContextListener on the start of the web-app
However, according to Opera Dragonfly (a debugging tool like Firebug), it appears that the server sends an HTTP 500 response about 30000ms after the request is sent.
Any responses created with userAsyncContext.getResponse().getWriter().print(SOME_JSON) and sent before the HTTP 500 response is not received by the browser, and I don't know why. Using the regular response object to send a response (response.print(SOME_JSON)) is received by the browser ONLY if all the code in the "if" statement dealing with AsyncContext is not present.
Can someone help me out? I have a feeling this is due to my misunderstanding of how the asynchronous API works. I thought that I would be able to store these AsyncContexts in a global map, then retrieve them and use their response objects to push things to the clients. However, it doesn't seem as if the AsyncContexts can write back to the clients.
Any help would be appreaciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我解决了这个问题。我的方法似乎存在几个问题:
在 Glassfish 中,AsyncContext 对象的默认超时时间均为 30,000 毫秒(0.5 分钟)。一旦该期限到期,整个响应将提交回客户端,这意味着您将无法再次使用它。
如果您正在实现长轮询,这可能不是什么大问题(因为您最终会在响应后发送另一个请求),但如果您希望实现流式传输(将数据发送回客户端)在不提交响应的情况下)您将需要增加超时时间,或者将其全部删除。
这可以通过 AsyncContext 的
.setTimeout()
方法来完成。请注意,虽然 规范 声明:“超时值为零或更少表示没有超时。”,Glassfish(此时)似乎将 0 解释为“需要立即响应”,将任何负数解释为“无超时”。如果您要实现流式处理,则在使用完
.print()
后,必须使用 printwriter 的.flush()
方法将数据推送到客户端。 code>.println()
或.write()
方法写入数据。在客户端,如果您已经流式传输数据,它将触发readyState为3(“交互式”,这意味着浏览器正在接收响应)。如果您使用 jQuery,则没有简单的方法来处理 3 的 readyStates,因此如果您要实现流式传输,则必须恢复到常规 Javascript,以便发送请求并处理响应。
I solved the issue. It seems as though there were several problems wrong with my approach:
In Glassfish, AsyncContext objects all have a default timeout period of 30,000 milliseconds (.5 minutes). Once this period expires, the entire response is committed back to the client, meaning you won't be able to use it again.
If you're implementing long-polling this might not be much of an issue (since you'll end up sending another request after the response anyway), but if you wish to implement streaming (sending data to back to the client without committing the response) you'll want to either increase the timeout, or get rid of it all together.
This can be accomplished with an AsyncContext's
.setTimeout()
method. Do note that while the spec states: "A timeout value of zero or less indicates no timeout.", Glassfish (at this time) seems to interpret 0 as being "immediate response required", and any negative number as "no timeout".If you're implementing streaming , you must use the printwriter's
.flush()
method to push the data to the client after you're done using its.print()
.println()
or.write()
methods to write the data.On the client side, if you've streamed the data, it will trigger a readyState of 3 ("interactive", which means that the browser is in the process of receiving a response). If you are using jQuery, there is no easy way to handle readyStates of 3, so you're going to have to revert to regular Javascript in order to both send the request and handle the response if you're implementing streaming.
我注意到,在 Glassfish 中,如果您使用 AsyncContext 并将 .setTimeOut() 设置为负数,无论如何连接都会断开,要解决此问题,我必须转到我的 Glassfish 管理 Web 配置器:asadmin set
configs.config.server-config.network-config.protocols.protocol.http-listener-1.http。并将超时设置为-1。所有这些都是为了避免 glassfish 在 30 秒后完成连接。
I have noticed that in Glassfish if you use AsyncContext and use .setTimeOut() to a negative number the connection is broken anyway, to fix this I had to go to my Glassfish admin web configurator : asadmin set
configs.config.server-config.network-config.protocols.protocol.http-listener-1.http. And set timeout to -1. All this to avoid glassfish finish the connections after 30 sec.