一. Servlet中的异步请求
在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理。如果要处理一些IO操作,以及访问数据库,调用第三方服务接口时,这种做法是十分耗时的。可以用代码测试一下:
同步方式处理:
@WebServlet("/helloServlet")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(Thread.currentThread()+"start...");
try {
sayHello();
} catch (InterruptedException e) {
e.printStackTrace();
}
resp.getWriter().write("hello...");
System.out.println(Thread.currentThread()+" end ....");
}
public void sayHello() throws InterruptedException {
Thread.sleep(3000);
}
}
以上代码,执行了一个3秒的方法,主线程在3秒方法执行完之后,才得到释放,这样处理效率非常不高。在Servlet 3.0引入了异步处理,然后在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。
异步方式处理业务逻辑:
@WebServlet(value = "/asyncServlet",asyncSupported = true)
public class AsyncHelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(Thread.currentThread()+"主线程start..."+System.currentTimeMillis());
//1.支持异步处理 asyncSupported = true
//2.开启异步
AsyncContext asyncContext = req.startAsync();
//3.业务逻辑业务处理
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread()+"副线程start..."+System.currentTimeMillis());
sayHello();
asyncContext.complete();
//4.获取响应
ServletResponse response = asyncContext.getResponse();
response.getWriter().write("hello,async...");
System.out.println(Thread.currentThread()+"副线程end..."+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
System.out.println(Thread.currentThread()+"主线程end..."+System.currentTimeMillis());
//测试结果,主线程接受请求处理,并立即得到释放,副线程处理业务逻辑
/**
* Thread[http-apr-8080-exec-9,5,main]主线程start...1545911791802
* Thread[http-apr-8080-exec-9,5,main]主线程end...1545911791806
* Thread[http-apr-8080-exec-10,5,main]副线程start...1545911791806
* Thread[http-apr-8080-exec-10,5,main]副线程end...1545911794807
*/
}
public void sayHello() throws InterruptedException {
Thread.sleep(3000);
}
上面的代码中,主线程在Servlet被执行到时,立刻得到了释放,然后在副线程中调用了sayHello方法,3秒后副线程得到释放,这就是异步Servlet最简单的一个demo演示。
二. springmvc中的异步请求
1.返回Callable<>接口
1)Spring异步处理,将Callable提交到TaskExecutor 使用一个隔离的线程进行执行。
2)DispacherServlet和所有的Filter退出线程,但是response保持打开状态
3)Callbale返回结果,springMVC将请求重新派发给容器,恢复之前的处理
4)根据Callable返回的结果,springMVC继续进行视图渲染流程等(从请求-视图渲染)。
代码如下:
@RequestMapping("/callable") @ResponseBody public Callable<String> async01(){ System.out.println("主线程"+Thread.currentThread()+"Start======>"+System.currentTimeMillis()); Callable<String> callable = new Callable<String>() { @Override public String call() throws Exception { System.out.println("副线程"+Thread.currentThread()+"Start======>"+System.currentTimeMillis()); Thread.sleep(3000); System.out.println("副线程"+Thread.currentThread()+"End======>"+System.currentTimeMillis()); return "Callable<String> async01()"; } }; System.out.println("主线程"+Thread.currentThread()+"End======>"+System.currentTimeMillis()); return callable; }
测试运行结果:
MyInterceptor preHandle.. 拦截器拦截的请求:/callable 主线程Thread[http-apr-8080-exec-9,5,main]Start======>1545965335805 主线程Thread[http-apr-8080-exec-9,5,main]End======>1545965335805 ==============DispacherServlet及所有的Filter退出线程============= ========================等待Callable执行======================== 副线程Thread[MvcAsync1,5,main]Start======>1545965335811 副线程Thread[MvcAsync1,5,main]End======>1545965338811 ========================Callable执行完成======================== MyInterceptor preHandle.. 拦截器拦截的请求:/callable MyInterceptor postHandle..(Callable的之前的返回值就是目标方法的返回值) MyInterceptor afterCompletion..
2.使用DeferredResult<>实现异步
public class DeferredResultQueue { public static Queue<DeferredResult<Object>> queue=new ConcurrentLinkedDeque<>(); public static void save(DeferredResult<Object> deferredResult){ queue.add(deferredResult); } public static DeferredResult<Object> get(){ return queue.poll(); } }
@Controller public class MyDeferredResultController{ /** * 一个线程创建订单,一个线程等待处理订单 * @return */ @ResponseBody @RequestMapping("/createOrder") public DeferredResult<Object> createOrder(){ //设置等待时间,3秒后等待超时 DeferredResult<Object> deferredResult = new DeferredResult<>((long)3000,"createFail"); DeferredResultQueue.save(deferredResult); return deferredResult; } @ResponseBody @RequestMapping("/create") public String create(){ String order = UUID.randomUUID().toString(); DeferredResult<Object> deferredResult = DeferredResultQueue.get(); deferredResult.setResult(order); return "success===>"+order; } }
上述代码模拟了创建订单的过程,运行时,先访问/createOrder请求,该请求会等待3秒(3秒期间内没有订单创建会超时),然后访问另一个请求/create,该请求创建号订单后,/createOrder立即接收到了刚创建好的订单,两者是异步执行的。