当前位置: 首页 > 面试题库 >

HttpServletResponse似乎会定期过早发送

谢承颜
2023-03-14
问题内容

我正在进行一个将http请求(用于测试目的的GET)发送到Java
Servlet的安装程序。它的工作方式是,服务程序从浏览器获取请求,进行解析,然后通过TCP套接字将其发送到“主”服务器,由“主”服务器处理请求并发送回响应。然后,该Servlet提取先前存储在ConcurrentHashMap中的HttpServletResponse,打开PrintWriter,然后将响应发送回去。一切都进行得很顺利,除了HttpServletResponse并不总是将写回信息的信息发送回PrintWriter。浏览器每次都会收到一个“
OK”响应,但是响应通常不包含我尝试编写的任何信息。

下面是初始化HttpServletResponse实例的初始doGet的代码,然后是写入Response缓冲区的方法。之后,还包括浏览器收到的响应。之后,对如何可靠地获得预期结果的一些观察,以帮助解决问题。

注意,唯一的变量似乎是响应是否被写入。我已经遍历了输出日志,并且在找到按预期方式编写响应的时间与未编写响应的时间之间找不到任何其他差异。我编写的HTTPServletResponseListener每次都会收到响应。

[使用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));
}

以及在响应时调用的HTTPServletResponseListener方法(除了仅将本地HttpServletResponse分配给本地字段的构造函数,这是唯一的方法)

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);         
     }
}

响应和浏览器收到它们:

当它按预期工作时:

当响应为空时:

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

如果响应是预期的:

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

观察结果:

通过执行以下步骤,我可以在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服务器外观,使其 返回 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);
  1. 使用Servlet 3.0异步支持,这在您的用例中是一个更好的选择,更改范围将非常有限。

doGet()

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

并在HTTPServletResponseListener一旦你完成:

    asyncContext.complete();

额外的调用startAsync()告诉容器:
即使我从回来了doGet(),我也没有完成此请求。请稍等。我前段时间写了一篇关于Servlet 3.0
的文章。



 类似资料:
  • 对于某些PDF,我在刷新标记结构时看到一个NPE。这个问题发生在iText 7.0.2-Snapshot中。iText 5.5.10可以很好地处理这些文件。NPE在中抛出,因为映射是空的。该类中的映射只有在调用时才能变为null。 因为的唯一目的是--就我所知--释放内存,所以我测试了当它更改为空方法时会发生什么。结果是文件似乎正常处理。不再有例外。下面是一个示例文件。 输出是什么:

  • 在Azure中,我开发了一个函数(应用服务),当新的csv文件放入特定存储帐户时会触发该函数。该函数是在Azure中开发的,每次上传新的csv文件时都不会出现问题。但是考虑到CI/CD,我决定将我的开发过程从Azure迁移到Visual Studio(2017)。 代码在本地运行没有任何问题,但一旦我将代码发布到Azure(通过VSTS),挑战就开始了。当新的csv文件上传到存储帐户时,触发器似乎

  • 我目前正在使用materialize CSS,似乎我已经遇到了选择字段。 我使用的是他们站点提供的示例,但不幸的是,它呈现在视图中。我想知道是否有其他人能帮上忙。 我要做的是创建一个有两个结尾间隔提供填充的行-然后在内部的两个行项中应该有一个搜索文本输入和一个搜索选择下拉列表。 这是我正在使用的示例:http://materializecss.com/forms.html 提前谢谢你。 下面是所讨

  • 问题内容: 问题 我有一个,以及一个带有属性的数据库对象列表,我想检查一下我的映射中的键是否等于数据库中的任何s- 如果是,请用填充。 代码看起来像这样: 但是,即使我确定有时会在该电话中,该电话始终会返回false。 调查中 作为健全性检查,我打印了日期的长值以及呼叫和呼叫的结果: 结果: 题 1)不应该和同意吗?(我假设至少应执行的建议)。 2)该医生说这个: 因此,当且仅当getTime方法

  • 我目前正在使用Spring Boot1.4.2,我在其中加入了Spring-boot-starter-web和spring-boot-starter-jpa。 我的主要问题是,当我保存一个新的实体时,它工作得很好(一切都很酷)。 但是,如果我用相同的id保存一个新的产品实体(例如重复的条目),它不会抛出异常。我期待的是ConstrintViolationException或类似的东西。 考虑到以下

  • 我有一个datatable,并将其导出为JSON格式。datatable的一列是日期列,如2013-01-09 02:18:11.117。