【问题标题】:Streaming output java流输出java
【发布时间】:2015-07-27 12:06:58
【问题描述】:

我有一个映射到 URL 的 servlet,它执行一项很长的任务并在工作时输出一些数据。

我想做的是调用这个 url 并实时查看输出。

我们以这个为例:

package com.tasks;

public class LongTaskWithOutput extends HttpServlet {
    private static final long serialVersionUID = 2945022862538743411L;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.addHeader("Content-Type", "text/plain");
        PrintStream out = new PrintStream(response.getOutputStream(), true);

        for(int i=0; i<10;i++) {
            out.println("# " + i);
            out.flush();
            try {
                Thread.sleep(1000);
            } catch(Exception e){}
        }
    }

}

web.xml:

...
<servlet>
    <servlet-name>LongTaskServlet</servlet-name>
    <servlet-class>com.tasks.LongTaskWithOutput</servlet-class>
    <description>Long Task Servlet</description>
</servlet>
<servlet-mapping>
    <servlet-name>LongTaskServlet</servlet-name>
    <url-pattern>/longTask</url-pattern>
</servlet-mapping>
...

会发生什么

如果我浏览localhost/myApp/longTask,浏览器会让我等待 10 秒,然后立即打印出所有文本。

会发生什么

文本一写入输出流就应该发送到浏览器,并且浏览器应该每秒渲染一行。

如您所见,我已经放了一个out.flush() 以确保流每秒刷新一次,但它仍然不起作用。

我也尝试了response.flushBuffer(),但结果相同。

有没有办法做到这一点?


更新

正如@MadConan 建议的那样,我尝试直接使用输出流:

OutputStream out = response.getOutputStream();

for(int i=0; i<10;i++) {
    out.write(("# " + i + "\n").getBytes());
    out.flush();
    try {
        Thread.sleep(1000);
    } catch(Exception e){}
}

不幸的是,结果还是一样。

【问题讨论】:

  • response.getOutputStream()获取的OutputStream具体实现是什么?
  • @MadConan 对于第一点,请查看更新。对于第二点,实现是org.apache.catalina.connector.CoyoteOutputStream(我用的是tomcat)
  • 我的直觉告诉我,这与您在缓冲区中放入的数据量有关,但如果不查看底层流的代码,我无法知道。即便如此,何时发送和使用数据最终取决于服务器和客户端的操作系统。
  • 我跟着弹跳球上了这堂课。并不是说它真的有帮助。 grepcode.com/file/repo1.maven.org/maven2/org.apache.tomcat/…
  • 您使用的是哪个版本的 Java EE?如果您使用的是 java-ee 7,则可以利用异步处理和非阻塞 I/O(如果不启用异步处理,这将无法工作)

标签: java jakarta-ee servlets output real-time


【解决方案1】:

这是上游问题。浏览器不一定会在收到数据时显示该数据。它可能会等到请求完成。如果您通过代理,您可能会有额外的分块。如果您窥探网络流量,我敢打赌它会按预期通过。

【讨论】:

  • If you snoop on the network traffic, I bet it will go through as expected. 我刚试过,我看到从服务器到客户端的 0 字节持续了 10 秒,然后一次性发送了一堆数据
  • 那么它可能处于较低级别,例如容器内。这可能发生在很多层面。
【解决方案2】:

您正在将内容刷新到响应流,但您的响应并未提交给客户端。以这种方式理解它 - 你正在给予一些东西成为某人,拿走和离开房间并移交给其他人。在此人离开房间并将其交给之前,您的包裹将不会送达。

根据您的要求,您可以继续将数据推送到某个全局空间,然后让客户端的 PULL 机制每 X 秒读取一次该空间并将内容显示给客户端。 也可以选择 PUSH 机制来做同样的事情,这取决于你的项目是使用 PUSH 还是 PULL。

这基本上意味着在一个请求中,您不能更新客户端并返回服务器并一遍又一遍地执行相同操作。一旦响应提交给客户端,请求就会终止。然后应该有另一个来自客户端的 PULL 请求或来自服务器的 PUSH。

【讨论】:

  • 我明白这一点,但我记得我前段时间用 php 做过类似的事情,它按预期工作。可能这与容器本身有关,而不是请求/响应?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-09
  • 1970-01-01
  • 2012-07-19
  • 2012-11-17
  • 2017-02-23
  • 2013-09-05
  • 2018-09-14
相关资源
最近更新 更多