gpt4 book ai didi

java - HttpServletResponse 似乎定期过早发送

转载 作者:行者123 更新时间:2023-12-04 03:07:03 33 4
gpt4 key购买 nike

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

下面我有用于传递 HttpServletResponse 实例的初始 doGet 的代码,然后是写入响应缓冲区的方法。之后包括浏览器收到的响应。之后,一些关于如何可靠地获得预期结果的观察,以防有助于确定问题。

请注意,唯一的变量似乎是响应是否被写入;我查看了输出日志,但在按预期写入响应的时间与未按预期写入响应的时间之间找不到任何其他差异。我写的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);
  3. 使用 Servlet 3.0 异步支持,这在您的用例中是一个更好的选择,并且更改的范围将非常有限。

    doGet() 中:

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

    完成后在 HTTPServletResponseListener 中:

    asyncContext.complete();

    startAsync() 的额外调用告诉容器:即使我已从 doGet() 返回,我还没有完成此请求。请等一下。我写了一个article前段时间关于Servlet 3.0的。

关于java - HttpServletResponse 似乎定期过早发送,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9316904/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com