直到现在,因为它适用于服务 http 请求,我认为术语 - asynchronous
和 non-blocking i/o
意思相同。但显然,它们分别在 servlet 3.0 和 3.1 中分别实现了。我很难理解这里的区别......
有人可以更清楚地说明这个话题吗?具体来说,我正在寻找一个服务器的 servlet 3.0 实现如何异步但阻塞线程的示例?我想如果我理解了这一点,可能更容易理解 servlet 3.1 中的非阻塞 i/o 试图解决的确切问题。
最佳答案
我会尽量总结我学到的东西。要理解 Servlet 3.0 和 Servlet 3.1 解决的问题,让我们这样看:
Servlet 3.0 之前:
同步处理请求的问题在于它导致线程(执行繁重的工作)在响应消失之前运行了很长时间。如果这种情况大规模发生,servlet 容器最终会耗尽线程——长时间运行的线程会导致线程饥饿。
在 Servlet 3.0 之前,对于这些长时间运行的线程有特定于容器的解决方案,我们可以在其中生成一个单独的工作线程来完成繁重的任务,然后将响应返回给客户端。 servlet线程启动worker线程后返回servlet池。 Tomcat的Comet、WebLogic的FutureResponseServlet和WebSphere的Asynchronous Request Dispatcher都是异步处理的一些实现例子。
(有关详细信息,请参阅 link 1。)
Servlet 3.0 异步:
实际工作可以委托(delegate)给线程池实现(独立于容器特定解决方案)。 Runnable
实现将执行实际处理,并将使用 AsyncContext
将请求分派(dispatch)到另一个资源或写入响应。我们也可以在AsyncContext对象中添加AsyncListener实现来实现回调方法。
(有关详细信息,请参阅 link 1。)
Servlet 3.1 NIO:
如上所述,Servlet 3.0 允许异步请求处理,但只允许传统 I/O(与 NIO 相对)。为什么传统 I/O 是个问题?
在传统的 I/O 中,有两种情况需要考虑:
- 如果进入服务器 (I/O) 的数据阻塞或流式传输速度比服务器读取速度慢,则尝试读取此数据的服务器线程必须等待该数据。
- 另一方面,如果从服务器写入
ServletOutputStream
的响应数据很慢,则客户端线程不得不等待。在这两种情况下,执行传统 I/O(用于请求/响应)的服务器线程都会阻塞。
换句话说,在 Servlet 3.0 中,只有请求处理部分变成了异步,而用于处理请求和响应的 I/O 则没有。如果有足够多的线程阻塞,则会导致线程饥饿并影响性能。
在 Servlet 3.1 NIO 中,这个问题通过 ReadListener
和 WriteListener
接口(interface)解决了。这些在 ServletInputStream
和 ServletOutputStream
中注册。监听器具有回调方法,当内容可供读取或可写入时调用,而 servlet 容器不会阻塞 I/O 线程。因此,这些 I/O 线程被释放,现在可以服务于其他提高性能的请求。
(有关详细信息,请参阅 link 2。)
致谢
关于Java - Servlet 3.0 中的异步与 Servlet 3.1 中的 NIO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39802643/