Spring注解驱动
Servlet3.0_异步请求
一、原生的Servlet3.0异步请求
1、3.0之前的弊端
在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式
处理请求。
即每一次Http请求都由某一个线程从头到尾负责处理
如果一个请求需要进行IO操作,比如访问数据库、调用第三方服务接口等,那么其所对应的线程将同步地等待IO操作完成, 而IO操作是非常慢的,所以此时的线程并不能及时地释放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题
。即便是像Spring、Struts这样的高层框架也脱离不了这样的桎梏,因为他们都是建立在Servlet之上的。为了解决这样的问题,Servlet 3.0引入了异步处理,然后在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。
每个线程处理完一个请求后才得到释放
测试模拟:
@WebServlet("/hello123")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(Thread.currentThread() + "start。。。。。。");
try {
sayGogo();
} catch (InterruptedException e) {
e.printStackTrace();
}
resp.getWriter().write("hello");
System.out.println(Thread.currentThread() + "end。。。。。。");
}
public void sayGogo() throws InterruptedException {
System.out.println(Thread.currentThread() + "processing。。。。。。");
Thread.sleep(3_000);//线程睡眠 3s
}
}
2、开启原生的异步模式
主线程收到请求后立即返回,并将接收到的请求交给异步线程,并由其完成业务,自己则就空闲,同一时间就会有更多的线程可供使用
@WebServlet(value = "/async",asyncSupported = true)
public class HelloAsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、支持异步asyncSupported = true
//2、开启异步模式
System.out.println("主线程开始。。。" + Thread.currentThread()+"===>" + System.currentTimeMillis());
AsyncContext async = req.startAsync();
//3、业务逻辑异步处理;开始异步处理
async.start(new Runnable() {
@Override
public void run() {
try {
System.out.println("副线程开始。。。" + Thread.currentThread()+"===>" + System.currentTimeMillis() );
sayGogo();
async.complete();
//获取到异步的上下文
AsyncContext asyncContext = req.getAsyncContext();
//获取响应
ServletResponse response = asyncContext.getResponse();
response.getWriter().write("hello async");
System.out.println("副线程结束。。。" + Thread.currentThread()+"===>" + System.currentTimeMillis());
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}
});
System.out.println("主线程结束。。。" + Thread.currentThread()+"===>" + System.currentTimeMillis());
}
public void sayGogo() throws InterruptedException {
System.out.println(Thread.currentThread() + "processing。。。。。。"+"===>" + System.currentTimeMillis());
Thread.sleep(3_000);
}
}
二、SpringMVC的异步处理
1、返回Callable
原理描述:
* 1、控制器返回Callable
* 2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
* 3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
* 4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
* 5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)。
测试:
@ResponseBody
@RequestMapping("/async01")
public Callable<String> async01(){
System.out.println("主线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
return "Callable<String> async01()";
}
};
System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
return callable;
}
结果输出
preHandle.../springmvc-annotation/async01
主线程开始...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
主线程结束...Thread[http-bio-8081-exec-3,5,main]==>1513932494700
=========DispatcherServlet及所有的Filter退出线程============================
================等待Callable执行==========
副线程开始...Thread[MvcAsync1,5,main]==>1513932494707
副线程开始...Thread[MvcAsync1,5,main]==>1513932496708
================Callable执行完成==========
================再次收到之前重发过来的请求========
preHandle.../springmvc-annotation/async01
postHandle...(Callable的之前的返回值就是目标方法的返回值)
afterCompletion...
2、返回DeferredResult
模拟业务逻辑:
应用1的线程1拿到请求,不创建订单,将请求放入消息中间件,应用2监听消息中间件,获取到加入消息中间件的请求,并创建订单,返回给消息中间件,线程2监听消息中间件,并获取消息中间件中创建好的订单,并返回出请求
模拟消息中间件:
public class DeferredResultQueue {
//创建队列
private static Queue<DeferredResult<Object>> queue = new ConcurrentLinkedDeque<>();
//将DeferredResult保存在队列中
public static void save(DeferredResult<Object> deferredResult){
queue.add(deferredResult);
}
//取出队列中的一个DeferredResult
public static DeferredResult<Object> getDeferredResult(){
return queue.poll();
}
}
模拟:
@Controller
public class AsyncController {
//模拟应用1获取到请求
@ResponseBody
@RequestMapping("/createOrder")
public DeferredResult<Object> createOrder(){
DeferredResult<Object> deferredResult = new DeferredResult<>(3000L,"create fail");
//模拟将获取的请求存入消息中间件中
DeferredResultQueue.save(deferredResult);
return deferredResult;
}
//模拟应用2处理请求创建订单
@ResponseBody
@RequestMapping("/create")
public String create(){
//创建订单
String order = UUID.randomUUID().toString();
//获取消息中间件中应用1存入的请求
DeferredResult<Object> deferredResult = DeferredResultQueue.getDeferredResult();
//模拟处理请求
deferredResult.setResult(order);
//模拟返回给消息中间件
return "success===>"+ order;
}
}
然后通过自己手动调用
localhost:8080//createOrder
来模拟应用1—等待应用2响应创建订单后返回请求
localhost:8080//create
来模拟应用2—获取消息中间件中的请求并处理
感谢尚硅谷!!!