在Servlet 3.0之前:
请求的同步处理的问题是,它会导致线程(执行繁重的任务)在响应发出之前运行很长时间。如果这种情况大规模发生,servlet容器最终会耗尽线程(长时间运行的线程会导致线程耗尽)。
在Servlet 3.0之前,对于这些长时间运行的线程,有一些特定于容器的解决方案,我们可以生成单独的工作线程来执行繁重的任务,然后将响应返回给客户端。启动工作线程后,Servlet线程返回到Servlet线程池。Tomcat的Comet,WebLogic的FutureResponseServlet和WebSphere的异步请求分派器是异步处理实现的一些示例。
Servlet 3.0 Async:
实际工作可以委托给线程池实现(独立于特定于容器的解决方案)。Runnable实现将执行实际的处理,并将使用AsyncContext将该请求分派到另一个资源或写入响应。我们还可以向AsyncContext对象添加AsyncListener实现来实现回调方法。
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接口解决。它们在ServletInputStream和ServletOutputStream中注册。侦听器具有回调方法,当内容可读或可写时调用,而不需要servlet容器阻塞I/O线程。因此,这些I/O线程被释放,现在可以为其他请求提供服务,从而提高性能。