2.13 Servlet3.1新增的非阻塞式IO
2.13 Servlet3.1新增的非阻塞式IO
最新的Java EE 7
已经发布,伴随Java EE 7
一起发布了Servlet3.1
,Servlet3.1
引入了少数新特性。Servlet3.1
新特性包括强制更改sessionId
(由HttpServletRequest
的changeSessionId()
方法提供)、非阻塞IO
等。尤其是Servlet3.1
提供的非阻塞IO进行输入、输出,可以更好地提升性能。Servlet
底层的IO
是通过如下两个IO
流来支持的。
ServletInputStream
:Servlet
用于读取数据的输入流。ServletOutputStream
:Servlet
用于输出数据的输出流。
以Servlet
读取数据为例,传统的读取方式采用阻塞式IO
——当Servlet
读取浏览器提交的数据时,如果数据暂时不可用,或数据没有读取完成,Servlet
当前所在线程将会被阻塞,无法继续向下执行。
ReadListener
从Servlet3.1
开始,ServletInputStream
新增了一个setReadListener(ReadListener readListener)
方法,该方法允许以非阻塞IO
读取数据,实现ReadListener
监听器需要实现如下三个方法
onAllDataRead()
:当所有数据读取完成时激发该方法。onDataAvailable()
:当有数据可用时激发该方法onError(Throwable t)
:读取数据出现错误时激发该方法。
类似地,ServletOuputStream
也提供了setWriterListenerer(WriteListener writeListener)
方法,通过这种方式,可以让ServletOuputStream
以非阻塞IO
进行输出
Servlet中使用非阻塞IO步骤
在Servlet
中使用非阻塞IO
非常简单,主要按如下步骤进行即可:
- 调用
ServletRequest
的startAsync()
方法开启异步模式 - 通过
ServletRequest
获取ServletInputStream
,并为ServletInputStream
设置监听器(ReadListener
实现类)。 - 实现
ReadListener
接口来实现监听器,在该监听器的方法中以非阻塞方式读取数据。
程序示例
项目结构
G:\Desktop\随书源码\轻量级Java EE企业应用实战(第5版)\codes\02\2.13\servlet31 ├─async.jsp ├─form.html └─WEB-INF\ ├─build.xml ├─classes\ │ └─lee\ │ ├─AsyncServlet.class │ └─MyReadListener.class ├─lib\ ├─src\ │ └─lee\ │ ├─AsyncServlet.java │ └─MyReadListener.java └─web.xml
下面的Servlet
负责处理表单页面提交的数据,但该Servlet
并未使用传统的、阻塞IO
来读取客户端数据,而是采用非阻塞IO
进行读取。下面是该Servlet
的代码。
AsyncServlet.java
1 | package lee; |
上面程序调用request
的startAsync()
方法开启异步调用之后,程序中粗体字代码为Servlet
输入流注册了一个监听器,这样就无须在该Servlet
中使用阻塞IO
来获取数据了。而是改为由MyReadListener
负责读取数据,这样Servlet
就可以继续向下执行,不会因为IO
阻塞线程。MyReadListener
需要实现Readlistener
接口,并重写它的三个方法。
MyReadListener.java
下面是MyReadListener
的代码。
1 | package lee; |
上面程序中MyReadListener
的onDataAvailable()
方法先暂停线程5秒,用于模拟耗时操作,接下来程序使用普通IO
流读取浏览器提交的数据。
如果程序直接让Servlet
读取浏览器提交的数据,那么该Servlet
就需要阻塞5秒,不能继续向下执行;改为使用非阻塞IO进行读取,虽然读取数据的IO
操作需要5秒,但它不会阻塞Servlet
执行,因此可以提升Servlet
的性能。
程序使用一个简单的表单向该Servlet
提交请求,该表单内包含了请求数据。提交的表单效果如图2.53所示。