避免等待Servlet流

maa*_*nus 19 java asynchronous servlets

我的Servler花了很多时间阅读request.getInputStream()和写作response.getOutputStream().从长远来看,这可能是一个问题,因为它阻止一个线程只是读/写字面上每秒几个字节.(*)

我从不对部分请求数据感兴趣,处理不应在请求完全可用之前启动.同样的回应.

我想,异步IO会解决它,但我想知道什么是正确的方法.也许一个servlet 在收集完整个输入之后用一个包装,使用和调用链式servlet 来Filter替换它?ServletInputStreamByteArrayInputStreamrequest.startAsync

  • 有没有这样的过滤器?
  • 我应该写一个还是应该使用不同的方法?

请注意,我的意思是避免在慢速servlet流上浪费线程.这与startAsync避免浪费线程等待某个事件的情况不同.

是的,目前这是一个不成熟的优化.

我的read循环按要求

我当前的输入流读取方法没有什么有趣的,但在这里你是:

private byte[] getInputBytes() throws IOException {
    ServletInputStream inputStream = request.getInputStream();
    final int len = request.getContentLength();
    if (len >= 0) {
        final byte[] result = new byte[len];
        ByteStreams.readFully(inputStream, result);
        return result;
    } else {
        return ByteStreams.toByteArray(inputStream);
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是全部,它在数据不可用时阻止; ByteStreams来自番石榴.

到目前为止我的理解总结

正如答案明确指出的那样,在不浪费线程的情况下使用servlet流是不可能的.servlet体系结构和通用实现都没有公开任何允许说"缓冲整个数据并仅在收集所有内容时给我打电话"的内容,尽管他们使用NIO并且可以做到.

原因可能是通常使用像nginx这样的反向代理,这可以做到.nginx默认执行此缓冲,直到两年前才关闭它.

实际上是受支持的案例???

鉴于许多负面答案,我不确定,但它看起来像我的目标

避免在慢速servlet流上浪费线程

实际上是完全支持的:从3.1开始,ServletInputStream.html#setReadListener似乎就是为了这个.分配用于处理的线程Servlet#Service最初调用request.startAsync(),附加侦听器并通过简单地返回返回池service.侦听器实现onDataAvailable(),当可以无阻塞地读取时调用它,添加一段数据并返回.在onAllDataRead(),我可以对收集的数据进行整个处理.

有一个例子,它如何用Jetty完成.它似乎也涵盖了非阻塞输出.


(*)在日志文件中,我可以看到花费在读取输入(100字节标题+ 100字节数据)上花费8秒的请求.这种情况很少见,但它们确实发生了,尽管服务器大多处于闲置状态.所以我猜,它是一个非常糟糕的连接上的移动客户端(我们的一些用户从连接不良的地方连接).

Bal*_*usC 12

HttpServletRequest#startAsync()对此没用.这只适用于推动网络套接字和良好的"上网"等内容.此外,JSR356 Web Socket API建立在它之上.

您的具体问题已被理解,但这绝对无法从servlet上解决.由于容器已经将当前线程专用于servlet请求,直到请求体完全读取到最后一位,即使它最终被新生成的读取,你最终只会浪费更多线程.异步线程.

要保存线程,您实际上需要一个支持NIO的servletcontainer,并在必要时启用该功能.使用NIO,单个线程可以处理与可用堆内存允许的TCP连接数相同的TCP连接,而不是每个TCP连接分配一个线程.然后,在您的servlet中,您根本不需要担心这个微妙的I/O任务.

几乎所有现代servlet容器都支持它:Undertow(WildFly),Grizzly(GlassFish/Payara),Tomcat,Jetty等.有些默认启用它,其他需要额外配置.只需使用关键字"NIO"参考他们的文档.

如果您实际上也想要保存servlet请求线程本身,那么您基本上需要退后一步,删除servlet并在现有NIO连接器(Undertow,Grizzly,Jetty,等等).

  • *"为每个请求分配线程而不是每个连接的线程"*这不是NIO的要点.如果没有NIO,您至少会浪费2个线程来处理这个缓慢的请求.一个用于TCP,一个用于servlet.使用NIO,您将"浪费"只有一个线程来处理这个慢速请求(一个用于servlet;一个用于TCP可以重用于其他连接/请求).但是你最终还是需要至少一个可用的线程.你还会以线程安全的方式处理请求体吗? (3认同)

use*_*421 6

  1. 你不能.Servlet容器将线程分配给请求,这就是它的结束,它已被分配.这就是模型.如果您不喜欢,则必须停止使用Servlet.
  2. 即使您可以解决(1),也无法在输入流上启动异步I/O.
  3. 处理慢速请求的方法是通过为你正在使用的任何容器设置适当的设置来计算它们的时间... 如果你确实遇到了问题,而且你真的很清楚,你的服务器大多是空闲的这种情况很少发生.
  4. 你的阅读循环有所区别.只需读取请求输入流到它的结尾.servlet容器已经确保流的末尾发生在内容长度(如果提供).

  • @Kin Async IO!==异步过程.OP只需要一个支持NIO的web/servlet容器.这不能通过servlet来控制. (2认同)