【问题标题】:Servlet long processing cancelation when browser closes浏览器关闭时 Servlet 长时间处理取消
【发布时间】:2010-10-05 22:36:23
【问题描述】:

我有一个长时间处理请求的 servlet。它假设 继续在 doPost 内的循环中做事并通过 回应的作家。有效地连续追加数据 客户端浏览器。 但是当客户端只是关闭浏览器时,问题就会出现。尽管 servlet 中响应的写入器流的断开连接 永远不会关闭,因此 servlet 不知道 brocen 连接, 并继续将数据转储到写入器中而不会出现任何错误。那个怎么样 可能吗?以及如何检测和取消长请求处理 浏览器断开连接的情况?

我认为响应编写器上的 checkError() 应该可以解决问题,但它无法正常工作。任何想法为什么?

这是永不停止的 servlet 代码:

protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
   HttpSession session = request.getSession();
   System.out.println("Session " + session.getId() + " started");

   response.setContentType("text/html;charset=UTF-8");

   PrintWriter out = response.getWriter();
   try
   {
       while (!out.checkError())
       {
           try
           {
               Thread.sleep(1000);
           } catch (InterruptedException ex)
           {
               ex.printStackTrace();
           }

           Date date = new Date();

           // TODO append output to the client browser here
           out.println(".....");

           System.out.println("Session " + session.getId() + "data sent at: " + date);

           out.flush();
       //break;  // _TEST
       }
   } finally
   {
       System.out.println("Session " + session.getId() + " finished");
       out.close();
   }
}

谢谢,
迈克

【问题讨论】:

    标签: java http servlets jakarta-ee browser


    【解决方案1】:

    解决这个问题的唯一真正方法是让浏览器不断告诉您它仍然存在,比如每 5-10 秒。如果你走了一分钟没有听到任何声音,你就认为客户已经走了,忘记了你在做什么。

    有几种方法可以实现:

    • 元刷新:我不喜欢这样,因为它让用户感到不安和明显;
    • 执行此操作的 iframe(不太明显);或
    • 一些 AJAX 技术,这可能是最好的选择。

    没有真正的方法可以检测客户端是否仍在阅读您发送的内容。 TCP 连接断开需要时间,而且到客户端的路径通常是间接的(通过代理等),所以忘记检测吧。

    【讨论】:

    • 看来你是对的。我必须有一些机制来从客户端发送 ACK 以指示服务器应该发送数据。这是不幸和奇怪的,因为 http 使用 TCP,因此假设是连接感知的。
    • 将 HTTP 描述为 TCP 是用词不当。当然 HTTP 协议是,但 Web 不是(必然)。考虑通过 UDP 通信和传递内容的 Squid 缓存。此外,您的 Web 应用程序可能会传递内容、关闭连接并且代理会丢失与浏览器的连接。
    • 这是否意味着如果您开始通过网络浏览器下载大文件,然后关闭它。服务器仍会使用流量或/和服务器资源向您发送整个文件?
    【解决方案2】:

    servlet 有一个输出缓冲区,必须在数据实际发送到浏览器之前填满。如果这个缓冲区没有被填满,那么网络上实际上没有数据发送。在网络上发送数据之前,您不会知道连接已关闭。

    您可以通过获取输出流或写入器之前调用响应对象上的方法来设置缓冲区大小。

    【讨论】:

    • 我已经尝试添加 response.setBufferSize(0);但它似乎没有任何效果。
    • 你不能把它设置为零,你可以把它设置为一些小的值,然后尝试写入缓冲区。
    【解决方案3】:

    您正在尝试执行 HTTP 协议不支持的操作。 Web 的整个本质都是基于请求/响应。

    模拟您需要的一种方法是使用 Ajax。 流程是这样的:

    1. 浏览器使用 Ajax 发送对项目 1 到 10 的请求
    2. 服务器处理请求、构建和发送响应,其中包含从 1 到 10 的项目。
    3. 浏览器接收响应,解析结果,显示项目1到10并重复步骤1,请求项目11到20。

    这种架构将消除检查客户端“可用性”的需要,还将提高服务器端组件的可伸缩性。

    希望对你有帮助。

    【讨论】:

      【解决方案4】:

      java.io.PrintWriter javadoc sez:此类中的方法从不抛出 I/O 异常 - 所以请改用 javax.servlet.SocketOutputStream (response.getOutputStream()),你最终会得到一些 IOException。

      【讨论】:

        【解决方案5】:

        您的一般方法对我有用(我有一个长时间运行的服务器任务,如果客户端挂断,使用checkError,在一些合理的音量发送失败后中止就好了)。我只能建议您更新到最新的 Java 版本。

        【讨论】:

          【解决方案6】:

          我们遇到了类似的问题。在我们的例子中,是启用了 ESI 的网络服务器 (http://www-01.ibm.com/support/docview.wss?uid=swg27015501)。我们做了一个 TCP 转储来查看那里发生了什么,并看到 RES 包(在浏览器关闭后)从未发送回应用程序。网络服务器收到了它,但没有进一步发送。因此,printer.checkError() 从未返回 true,并且打印机一直在刷新数据。 禁用此功能后,RES 包被发送到应用程序,一段时间后,printer.checkError() 返回 true。 这就是为什么我偶然发现了你的问题。它一直发送3次。之后它 checkError() 返回 true。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-01-26
            • 2016-01-06
            • 2020-12-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多