服务器资源是有价值的,应谨慎使用。考虑一个servlet 必须等待一个JDBC连接,或等待接收JMS消息 或从文件系统读取的资源。等待一个“长期运行”过程返回会引起 线程完全阻塞。异步
处理在在等待长时间运行的过程同时,可以使控制(或线程)返回到容器来继续执行其他
任务。
@WebServlet(urlPatterns="/async",asyncSupported=true)
public class MyAsyncServlet extends HttpServlet {
}
也可以在 web.xml定义<async-supported>为true。
也可以在 web.xml定义<async-supported>为true。
然后,您可以在单独的线程使用request的同步放startA方法启动异步处理
,此方法返回AsyncContext,它代表了 异步请求的执行上下文。然后你就可以通过调用AsyncContext.complete完成异步 ,或者派遣到另一个请求 资源(隐式)。容器将在后台完成异步请求的调用。
假设有一个需要长时间运行的任务:
class MyAsyncService implements Runnable {
AsyncContext ac;
public MyAsyncService(AsyncContext ac) {
this.ac = ac;
}
@Override
public void run() {
//. . .
ac.complete();
}
}
这个任务可以在servlet中被调用异步运行:
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
AsyncContext ac = request.startAsync();
ac.addListener(new AsyncListener() {
public void onComplete(AsyncEvent event)
throws IOException {
//. . .
}
public void onTimeout(AsyncEvent event) throws IOException {
//. . .
}
//. . .
});
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);
executor.execute(new MyAsyncService(ac));
}
该请求被放入异步模式。 当请求处理完成后,AsyncListener被注册 侦听事件,或已超时,或导致一个错误。长期运行的服务在一个单独的线程异步调用,完成请求处理调用Context.complete。
Servlet 3.0中允许异步请求处理,但只允许传统 I / O,这限制了应用程序的可扩展性。
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
ServletInputStream input = request.getInputStream();
byte[] b = new byte[1024];
int len = -1;
while ((len = input.read(b)) != -1) {
//. . .
}
}
如果传入的数据流比服务器更慢,那么 服务器线程就在等待数据。如果数据被写入,相同的可能也会发生,这限制了Web容器的可扩展性。
非阻塞I / O使得Web容器不仅可伸缩,也可以同时处理更多连接数量。非阻塞 I / O只能用异步请求处理的Servlet,过滤器。Servlet3.1实现了非阻塞I / O,通过引入两个新的接口:ReadListener和WriteListener。这些监听者有回调方法,可以在 内容可被读取或可写入而不阻塞时调用。前面案例重写为:
AsyncContext context = request.startAsync();
ServletInputStream input = request.getInputStream();
input.setReadListener(new MyReadListener(input, context));
ReadListener有三个回调方法:
onDataAvailable回调方法是数据可以被读取时调用
onAllDataRead回调方法是当请求数据 完全读取时调用。
OnError回调是如果有一个错误处理请求时被调用
@Override
public void onDataAvailable() {
try {
StringBuilder sb = new StringBuilder();
int len = -1;
byte b[] = new byte[1024];
while (input.isReady() && (len = input.read(b)) != -1) {
String data = new String(b, 0, len);
}
} catch (IOException ex) {
//. . .
}
}
@Override
public void onAllDataRead() {
context.complete();
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
context.complete();
}
ServletInputStream.isReady方法用于检查数据可以被读取而不会阻塞,然后数据被读出。 context.complete在 onAllDataRead和onError方法读取数据的完成后调用。 Servle
tInputStream.isFinished可以用来检查一个非阻塞I/ O读取的状态。
WriteListener有两个回调方法:
onWritePossible是可以无堵塞写入数据被调用时
onerror的是如果有错误处理响应时被调用
最多只有一个WriteListener可以在ServletOutputStream注册。 ServletOut putStream.canWrite是一种新的方法检查数据是否可以不阻塞地被写入。