HttpServletResponse似乎周期性发送过早 [英] HttpServletResponse seems to periodically send prematurely

查看:152
本文介绍了HttpServletResponse似乎周期性发送过早的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个将HTTP请求(GET用于测试目的)安装到java servlet的设置。它的工作原理是,服务器从浏览器接收一个请求,解析并通过TCP套接字发送给'main'服务器,该服务器处理请求并发回响应。然后,servlet将先前存储在ConcurrentHashMap中的HttpServletResponse取出,打开PrintWriter并发回响应。一切都很顺利,除了HttpServletResponse并不总是发送写入PrintWriter的信息。浏览器每次都会收到一个OK响应,但通常响应中不包含任何我尝试写入的信息。

下面我有关于初始doGet的代码,它取消了HttpServletResponse实例,然后是写入Response缓冲区的方法。其中包括浏览器接收到的响应。之后,一些关于如何可靠地获得预期结果的意见,以防止问题得到解决。



请注意,唯一的变量似乎是响应是否被写入;我倾倒了输出日志,并且无法发现响应按预期写入的时间与不响应的时间之间的任何其他差异。我写的HTTPServletResponseListener每次都会收到响应。

使用Glassfish 3.1.1

  protected void doGet(HttpServletRequest请求,HttpServletResponse响应)抛出ServletException,IOException {

String js = request.getParameter(json);
//省略try-catch for space
消息msg = this.parser.parseToMessage(js);
this.sc.send(msg,new HTTPServletResponseListener(response));
}

HTTPServletResponseListener方法在响应时被调用(除了只有构造函数

  public void handleResponse(ResponseMessage response){
DataParser parser = new JSONParser();
String temp = parser.parseToString(response);
httpResponse.setContentType(application / json);
httpResponse.addHeader(Hmm,yup);
尝试{
PrintWriter out = httpResponse.getWriter();
out.println(temp);
} catch(IOException ex){
Logger.getLogger(HTTPServletReponseListener.class.getName()).log(Level.SEVERE,null,ex);


响应和浏览器收到它们:



当它按预期工作时:



当回应为空时:

  HTTP / 1.1 200 OK 
X-Powered-By:Servlet / 3.0 JSP / 2.2(GlassFish Server Open Source Edition 3.1.1 Java / Sun Microsystems Inc./1.6 )
服务器:GlassFish服务器开源版本3.1.1
内容长度:0
日期:2012年2月16日星期四15:26:35 GMT


  HTTP / 1.1 200 OK 
Hmm:yup
X-Powered-By:Servlet / 3.0 JSP / 2.2(GlassFish Server开源版3.1.1 Java / Sun Microsystems Inc./1.6)
服务器:GlassFish服务器开源版本3.1.1
内容类型:application / json; charset = ISO-8859-1
内容长度:126
日期:2012年2月16日,星期四15:27:30 GMT

观察:

<我可以通过follo得到95%的回复这些步骤......否则,其成功率约为50%。

刷新浏览器......部署后的第一对情侣请求往往更经常地工作
等待至少5秒再次刷新刷新(发送另一个测试请求)
,这往往可靠地工作。如果它失败了,你必须等待15秒,然后它会再次工作。如果HttpServletResponse是在等待响应之前写入的,那么它每次都有效。



非常感谢您花时间阅读本文。我已经阅读了其他的Stackoverflow问题,但似乎没有涉及到这个问题,除非我错过了一个连接。

>这是最有趣的一行:

  this.sc.send(msg,new HTTPServletResponseListener(response)); 

我怀疑 sc 是这个外部TCP你正在调用的服务器,并且你还传递一个监听器,以便在响应到达时得到通知。现在有一个重要的假设:我是正确的,TCP服务器异步发送响应,在不同线程中通知您的监听器?



如果是这样,它解释你的行为。在3.0之前的servlet中,你必须在 doGet 中处理整个请求。一旦你的代码离开了 doGet(),servlet容器就会假设整个请求已经被处理并且放弃了请求。



您已经引入了争用条件 doGet()返回,而不向输出流写入任何内容。容器处理响应并将其发送回来需要几毫秒的时间。如果在这段短时间内你的外部TCP服务器返回数据并通知监听器,数据将通过。但是,如果服务器速度较慢,那么您正在向已处理的连接发送响应。



以这种方式考虑:浏览器发出呼叫, doGet()被调用,然后调用后端服务器。 doGet()返回并且servlet容器假定您完成了。它发回(空)响应并忘记了这个请求。毫秒或甚至几秒钟后,响应从后端服务器返回。但是连接没有了,它已经被发送,套接字被关闭,浏览器呈现响应。你没有告诉你的容器:嘿,等等,我还没有完成这个回应!



解决方案



从最坏的到最好的:


  1. 积极等待/轮询 doGet()


  2. 使您的外部TCP服务器呼叫阻止。更改TCP服务器Facade以使其返回 ResponseMessage ,并将侦听器代码移动到 doGet()。示例:

      ResponseMessage responseMsg = this.sc.send(msg); 
    DataParser parser = new JSONParser();
    String temp = parser.parseToString(responseMsg);
    httpResponse.setContentType(application / json);
    httpResponse.addHeader(Hmm,yup);
    PrintWriter out = httpResponse.getWriter();
    out.println(temp);


  3. 使用Servlet 3.0异步支持,在您的用例和
    $ b

    doGet()

      final AsyncContext asyncContext = request.startAsync(request,response); 

    以及 HTTPServletResponseListener done:

      asyncContext.complete(); 

    额外调用 startAsync() tell容器:尽管我已经从 doGet()返回,但我还没有完成这个请求。请握住。我写了一段关于Servlet 3.0的文章前。


I'm working on a setup that takes an http request (GET for testing purposes) to java servlet. How it works is, the ser let takes a request from the browser, parses it and sends it via TCP socket to the 'main' server, which processes the request and sends a response back. The servlet then pulls the HttpServletResponse that was previously stored in an ConcurrentHashMap, opens up the PrintWriter, and sends a response back. Everything's going smoothly, except that the HttpServletResponse doesn't always send back the information written to the PrintWriter. The browser receives an "OK" response every time, but often the response doesn't contain any of the information I try to write.

Below I have the code for the initial doGet that hands off the HttpServletResponse instance,then the method that writes to the Response buffer. Included after that are the responses as the browser receives them. After that, some observations on how to reliably get the expected result, in case that helps pin down the problem.

Note that the only variable seems to be whether or not the Response gets written; I've poured over output logs, and can't find any other differences between the times where the Response is written as expected, and the times it isn't. The HTTPServletResponseListener I wrote receives the response every time.

[using Glassfish 3.1.1]

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

String js = request.getParameter("json");
// Omitting try-catch for space
Message msg = this.parser.parseToMessage(js);
this.sc.send(msg, new HTTPServletResponseListener(response));
}

And the HTTPServletResponseListener method that is called on response (other than the constructor which only assigns the local HttpServletResponse to a local field, this is the only method)

public void handleResponse(ResponseMessage response) {
  DataParser parser = new JSONParser();
  String temp = parser.parseToString(response);
  httpResponse.setContentType("application/json");
  httpResponse.addHeader("Hmm","yup");
  try {
     PrintWriter out = httpResponse.getWriter();
     out.println(temp);
     }  catch (IOException ex) {
        Logger.getLogger(HTTPServletReponseListener.class.getName()).log(Level.SEVERE,null,ex);         
     }
}

Responses and the browser receives them:

When it works as intended:

When the response is empty:

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.1 Java/Sun         Microsystems Inc./1.6)
Server: GlassFish Server Open Source Edition 3.1.1
Content-Length: 0
Date: Thu, 16 Feb 2012 15:26:35 GMT

When the response is as intended:

HTTP/1.1 200 OK
Hmm: yup
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.1 Java/Sun  Microsystems Inc./1.6)
Server: GlassFish Server Open Source Edition 3.1.1
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 126
Date: Thu, 16 Feb 2012 15:27:30 GMT

Observations:

i can get a response 95% of the time by following these steps.... otherwise, its around 50% success rate.

hit refresh on browser... the first couple requests after deploying tend to work more often wait at least 5 seconds before hitting refresh again (sending another test request) this tends to work reliably. If it fails, you have to wait 15 seconds, and then it tends to work again.

if the HttpServletResponse is written BEFORE waiting for the response, it works every time.

Thank you so much for taking the time to read this. I've read other Stackoverflow questions, but nothing seems to touch on this particular problem, unless I'm missing a connection.

解决方案

This is the most interesting line:

this.sc.send(msg, new HTTPServletResponseListener(response));

I suspect the sc is this external TCP server you are calling and you are also passing a listener to be notified when the response arrives. Now the important assumption: am I right that the TCP server sends response asynchronously, notifying your listener in a different thread?

If this is the case, it explains your behaviour. In pre-3.0 servlets you had to handle the whole request within doGet. Once your code leaves doGet(), servlet container assumes the whole request has been handled and discards the request.

You have introduced a race condition: doGet() returns without writing anything to the output stream. It takes few milliseconds for the container to handle the response and send it back. If during this short period of time your external TCP server returns data and notifies listener, the data will go through. But if the server is a bit slower, you are sending response to a connection that has already been handled.

Think about it this way: browser makes a call, doGet() is called which in turns calls backend server. The doGet() returns and servlet container assumes you are done. It sends back (empty) response and forgets about this request. Milliseconds or even seconds later response comes back from the back-end server. But the connection is gone, it has already been sent, sockets were closed, browser rendered the response. You are not telling your container: hey, wait, I haven't finished with that response!.

Solutions

From worst to best:

  1. Actively wait/poll for response in doGet().

  2. Make your external TCP server call blocking. Change the TCP server facade so that it returns ResponseMessage and move listener code to doGet(). Example:

     ResponseMessage responseMsg = this.sc.send(msg);
     DataParser parser = new JSONParser();
     String temp = parser.parseToString(responseMsg);
     httpResponse.setContentType("application/json");
     httpResponse.addHeader("Hmm","yup");
     PrintWriter out = httpResponse.getWriter();
     out.println(temp);
    

  3. Use Servlet 3.0 asynchronous support, it is a much better choice in your use case and the scope of changes will be very limited.

    In doGet():

    final AsyncContext asyncContext = request.startAsync(request, response);
    

    and in the HTTPServletResponseListener once you're done:

    asyncContext.complete();
    

    The extra call to startAsync() tells the container: even though I have returned from doGet(), I haven't finished with this request. Please hold. I wrote an article about Servlet 3.0 some time ago.

这篇关于HttpServletResponse似乎周期性发送过早的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆