我的Servler花了很多时间阅读request.getInputStream()
并写信给response.getOutputStream()
。从长远来看,这可能是一个问题,因为它阻止一个线程,除了每秒几个字节读/写字面。 (*)
我从不对部分请求数据感兴趣,处理不应在请求完全可用之前启动。同样的回应。
我想,异步IO会解决它,但我想知道什么是正确的方法。也许servlet Filter
用ServletInputStream
替换ByteArrayInputStream
,使用request.startAsync
并在收集整个输入后调用链式servlet?
请注意,我的意思是避免在慢速servlet流上浪费线程。这与startAsync
不同,它可以避免浪费线程等待某个事件。
是的,目前它还不成熟。
我当前的输入流读取方法没有什么有趣的,但在这里你是:
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);
}
}
全部,当数据不可用时,它会阻止; ByteStreams
来自番石榴。
正如答案明确指出的那样,在不浪费线程的情况下使用servlet流是不可能的。 servlet体系结构和通用实现都没有公开任何允许说“缓冲整个数据”的内容,只有当你收集了所有内容时才打电话给我,尽管他们使用NIO并且可以这样做。
原因可能是通常使用像nginx这样的反向代理,这可以做到。 nginx默认执行此缓冲,甚至在two years ago之前无法关闭。
鉴于许多否定答案,我不确定,但它看起来像我的目标
避免在慢速servlet流上浪费线程
实际上完全支持:从3.1开始,ServletInputStream.html#setReadListener似乎就是为了这个。分配用于处理Servlet#Service
的线程最初调用request.startAsync()
,附加侦听器并通过从service
返回返回池。监听器实现onDataAvailable()
,当它可以读取而不阻塞时会被调用,添加一段数据并返回。在onAllDataRead()
中,我可以对收集的数据进行整个处理。
有一个example,如何用Jetty完成。它似乎也涵盖了非阻塞输出。
(*)在日志文件中,我可以看到花费在读取输入(100字节标题+ 100字节数据)上花费8秒的请求。这种情况很少见,但它们确实发生了,尽管服务器大多处于闲置状态。所以我猜,它是一个连接非常糟糕的移动客户端(我们的一些用户从连接不良的地方连接)。
答案 0 :(得分:12)
HttpServletRequest#startAsync()
对此无用。这对于推动诸如网络套接字和优秀的SSE之类的东西非常有用。此外,JSR356 Web Socket API建立在它之上。
您的具体问题已被理解,但这绝对无法通过servlet解决。由于容器已经将当前线程专用于servlet请求,直到请求主体被完全读取到最后一位,即使它已经完成,你最终只会浪费更多线程。最终由新生成的异步线程读取。
要保存线程,您实际上需要一个支持NIO的servletcontainer,并在必要时启用该功能。使用NIO,单个线程可以处理与可用堆内存允许的TCP连接数相同的TCP连接,而不是每个TCP连接分配一个线程。然后,在你的servlet中,你根本不需要担心这个微妙的I / O任务。
几乎所有现代servletcontainers都支持它:Undertow(WildFly),Grizzly(GlassFish / Payara),Tomcat,Jetty等等。有些人默认启用它,其他人需要额外配置。只需使用关键字" NIO"。
推荐他们的文档如果您实际上也想保存servlet请求线程本身,那么您基本上需要退一步,删除servlet并在现有NIO连接器上实现基于NIO的自定义服务(Undertow,Grizzly,Jetty等)。
答案 1 :(得分:6)
答案 2 :(得分:2)
有一个名为org.apache.catalina.connector.CoyoteAdapter
的类,它是从TCP工作线程接收封送请求的类。它有一个名为" service"这是繁重的大部分工作。此方法由另一个类调用:org.apache.coyote.http11.Http11Processor
,它也有一个同名的方法。
我觉得有趣的是,我在代码中看到如此多的钩子来处理异步io,这让我想知道这不是容器的内置功能吗?无论如何,凭借我有限的知识,我能想到实现你所讨论的功能的最佳方式是创建一个类:
public class MyAsyncReqHandlingAdapter extends CoyoteAdapter
和@Override service()
方法并推出自己的方法......我现在没有时间专心做这件事,但我将来可能会再次访问。
在这种方法中,您需要一种方法来识别慢请求并处理它们,方法是将它们交给单线程nio处理器并完成"完成"在该级别的请求,给定源代码:
你应该能够弄明白该怎么做。有趣的问题,是的,它可以做到。我没有在规范中看到它说它不能......