【发布时间】:2017-07-06 11:38:05
【问题描述】:
我的 Servler 花费了相当长的时间阅读 request.getInputStream() 并写入 response.getOutputStream()。从长远来看,这可能是一个问题,因为它阻塞线程只是为了每秒读取/写入 literally 几个字节。 (*)
我对部分请求数据从不感兴趣,在请求完全可用之前不应开始处理。响应也是如此。
我想,异步 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 秒。这种情况很少见,但确实会发生,尽管服务器大多处于空闲状态。所以我猜,这是一个连接非常糟糕的移动客户端(我们的一些用户从连接如此糟糕的地方连接)。
【问题讨论】:
-
Servlet 3.0 已经为 Servlet 提供了异步 API。您不必自己动手。
-
@EJP 是的,但是 servlet 异步 API 是关于在等待某事时释放线程。在这里,我的意思是异步 IO,即在读取慢速流(恰好是 servlet 流)时不浪费线程。一个实现将使用这两种方式作为异步。
-
我真的不明白为什么你的问题附有赏金?如果你想学习如何编写一个 nio web 服务器,那就去做并测试它......已经有很多示例和代码,只需谷歌“java nio web server”...... Async io 无法解决这个事实您必须等待 tcp 驱动程序将数据包编组为字节,然后您的 java 类才能读取它们?如果我错了,我错过了什么?
-
向我们展示您的读/写循环代码。
-
在现实生活中只需在 servlet 引擎之前使用 nginx。见:https://stackoverflow.com/questions/28166284/does-nginx-also-buffer-http-request-from-client
标签: java servlets asynchronous