1、为什么要使用异步Servlet?
非常适用于以下情况的Web应用程序
—— 长处理时间或者伪长处理时间
—— 等待资源释放——如数据库连接
—— 等待事件发生——如聊天消息
—— 等待缓慢服务的响应——如Web服务
释放当前请求处理以便处理其他请求
—— 为Web容器提供了更好的可伸缩性
浏览器将仍然显示为等待中
—— 用户体验没有改变
不要与异步I/O混淆
——目前的Servlet3.0不支持
2、AsyncContext 基本知识
调用HttpServletRequest.startAsync()将servlet置于异步模式
—— 流将不在从Servlet.service()返回时提交
—— 返回AsyncContext对象
AsyncContext.start()启动请求
—— 将其指派给工作线程
AsyncContext.dispatch()
—— 通知工作已经完成
—— 将控件传递回Web容器
在以前的Servlet规范化,如果Servlet作为控制器调用了一个耗时的业务方法,那么必须等到业务方法完全返回之后才能生成响应,这将使用Servlet对业务方法的调用变成一种阻塞式的调用,因此效率比较低。
Servlet3.0规范引入了异步处理来解决这个问题,异步处理允许Servlet重新发起一条新线程去调用 耗时业务方法,这样就可以避免等待。
Servlet3.0的异步处理是通过AsyncContext类来处理的,Servlet可通过ServletRequest的如下两个方法开启异步调用,创建AsyncContext对象:
AsyncContext startAsync()
AsyncContext startAsync(ServletRequest,ServletResponse)
3、Servlet3.0异步请求实例
这个例子只有2文件,一个Servlet和一个Jsp,代码如下:
AsynServlet.java
package org.test;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @ClassName: AsynServlet
* @description: 使用批注的方式,指定这个Servlet的映射URL,后面那个配置是设置其是支持异步请求
*
* 1、其它创建一个异步请求和我们平常使用Servlet没什么区别,当然Filter也可以设置为异步的。
* 2、但是在使用异步请求,我们需要注意一些东西:
* 1)异步请求完成后指定向的JSP页面,session必须设定为false
* 2)AsyncListener监听中监听开始在实际无效
* 3)如果存在过滤器过滤该Servlet,则必须把Filter也要设置为asyncSupported=true,否则会出现异常,提示不支持异步
*
* @createDate: 2013-5-30 下午4:59:31
* @author linyaoliang
* @version
*/
@WebServlet(urlPatterns="/async",asyncSupported=true)
public class AsynServlet extends HttpServlet
{
private static final long serialVersionUID = 5419388337018572479L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/html;charset=GBK");
PrintWriter out = resp.getWriter();
out.println("<title>异步调用示例</title>");
out.println("进入Servlet的时间:"+new java.util.Date()+".<br>");
out.flush();
if(req.getAsyncContext() != null)
{
System.out.println("异步上下文已经存在");
}
//创建AsyncContext,开始异步调用
AsyncContext actx = req.startAsync();
actx.addListener(new MyAsyncListener());
//设置异步调用的超时时长
actx.setTimeout(30*1000);
//异步请求上下文启动异步处理线程
actx.start(new Executor(actx));
//启动后,输出结束时间
out.println("结束Servlet的时间:"+new java.util.Date()+".<br>");
out.flush();
}
/**
*
* @ClassName: Executor
* @description: 这个是异步请求需要启动来处理长时间业务的线程
* @createDate: 2013-5-30 下午4:56:55
* @author linyaoliang
* @version AsynServlet
*/
class Executor implements Runnable{
private AsyncContext actx = null;
public Executor(AsyncContext actx)
{
this.actx = actx;
}
@Override
public void run()
{
try
{
System.out.println("====================");
Thread.sleep(5*1000);
ServletRequest request = actx.getRequest();
List<String> books = new ArrayList<String>();
books.add("Java Programer");
books.add("Strut Spring hibernate");
books.add("Test Action");
request.setAttribute("books", books);
System.out.println("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||");
//当该线程完成后,则返回指定的到这个JSP页面,这个JSP页面必须指定session="false",在这个JSP页面里你可以看到
actx.dispatch("/async.jsp");
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
*
* @ClassName: MyAsyncListener
* @description: 异步请求提供的监听
* @createDate: 2013-5-30 下午4:54:58
* @author linyaoliang
* @version AsynServlet
*/
class MyAsyncListener implements AsyncListener
{
@Override
public void onComplete(AsyncEvent arg0) throws IOException
{
System.out.println("异步请求已经完成:"+arg0.getAsyncContext().getTimeout());
}
@Override
public void onError(AsyncEvent arg0) throws IOException
{
System.out.println("异步请求出错了:"+arg0.getAsyncContext().getTimeout());
}
@Override
public void onStartAsync(AsyncEvent arg0) throws IOException
{
//虽然提供了这个监听方法,但是实际没用,因为都是在已经启动异步请求后,得到AsynContext,再去绑定这个监听,可以在上面的代码中看到
System.out.println("异步请求已经开始:"+arg0.getAsyncContext().getTimeout());
}
@Override
public void onTimeout(AsyncEvent arg0) throws IOException
{
System.out.println("异步请求已经超时:"+arg0.getAsyncContext().getTimeout());
}
}
}
async.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" session="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>异步响应的消息</title>
</head>
<body>
<ul>
<c:forEach items="${books }" var="book">
<li>${book }</li>
</c:forEach>
</ul>
<%out.println("业务调用结束时间:"+new java.util.Date()); %>
</body>
</html>