一尘不染

尽管调用了flush(),但Servlet缓冲了响应

tomcat

我们有一个系统,其中客户端发出HTTP
GET请求,系统在后端进行一些处理,压缩结果,然后将其发送给客户端。由于处理可能需要一些时间,因此我们将其作为ZipOutputStream包装发送response.getOutputStream()

但是,当第一个ZipEntry条目中的数据量非常少,而第二个条目需要很长时间时,客户端使用的浏览器就会超时。我们已经尝试刷新流缓冲区,但是似乎没有响应发送到浏览器,直到将至少1000个字节写入流中为止。奇怪的是,一旦发送了前1000个字节,随后的刷新似乎可以正常工作。

我尝试将代码简化为一个简单的例子:

protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    try {
        ZipOutputStream _zos = new ZipOutputStream( response.getOutputStream());
        ZipEntry _ze = null;
        long startTime = System.currentTimeMillis();
        long _lByteCount = 0;

        response.setContentType("application/zip");

        while (_lByteCount < 2000) {
            _ze = new ZipEntry("foo");
            _zos.putNextEntry( _ze );

            //writes 100 bytes and then waits 10 seconds
            _lByteCount += StreamWriter.write( 
                    new ByteArrayInputStream(DataGenerator.getOutput().toByteArray()),
                    _zos );
            System.out.println("Zip: " + _lByteCount + " Time: " + ((System.currentTimeMillis() - startTime) / 1000));

            //trying to flush
            _zos.finish();
            _zos.flush();
            response.flushBuffer();
            response.getOutputStream().flush();
        }
    } catch (Throwable e) {
        e.printStackTrace();
    }
}

我将浏览器超时设置为约20秒,以便于复制。尽管几次写入100个字节,但没有任何内容发送到浏览器,并且浏览器超时。如果我扩大了浏览器的超时时间,则直到写入1000个字节后,任何内容都不会发送,然后浏览器会弹出“您想保存…”对话框。同样,在最初的1000个字节之后,每增加100个字节就会发送一次,而不是缓冲到1000个字节的块。

如果我将while条件中的最大字节数设置为200左右,则可以正常工作,仅发送200个字节。

我该怎么做才能迫使Servlet发回很少的初始数据?


阅读 487

收藏
2020-06-16

共1个答案

一尘不染

事实证明,在底层Apache / Windows IP堆栈上存在一个限制,该限制可以缓冲流中的数据以提高效率。由于大多数人都有的问题 太多
的数据,而不是问题 太少
的数据,这是最正确的时间。我们最终要做的是要求用户请求足够的数据,以便在超时之前达到1000字节的限制。很抱歉花了这么长时间回答这个问题。

2020-06-16