Spring注解开发系列Ⅸ --- 异步请求
一. 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立即接收到了刚创建好的订单,两者是异步执行的。
Spring注解开发系列Ⅸ --- 异步请求的更多相关文章
- Spring注解开发系列Ⅵ --- AOP&事务
注解开发 --- AOP AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,横向重复,纵向抽取.详细的AO ...
- Spring注解开发系列专栏
这个系列主要是讲Spring注解的使用,可以为后面SpringBoot的学习带来一定的帮助.我觉得从Spring直接过度到SpringBoot还是有点快,还是得需要一个演变的过程.从Spring开发, ...
- Spring注解开发系列VIII --- SpringMVC
SpringMVC是三层架构中的控制层部分,有过JavaWEB开发经验的同学一定很熟悉它的使用了.这边有我之前整理的SpringMVC相关的链接: 1.SpringMVC入门 2.SpringMVC进 ...
- Spring注解开发系列VII --- Servlet3.0
Servlet3.0简介 Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布.该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用 ...
- Spring注解开发系列Ⅰ--- 组件注册(上)
传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点:1.如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大:如果按需求分开.xml文件 ...
- Spring注解开发系列Ⅴ --- 自动装配&Profile
自动装配: spring利用依赖注入和DI完成对IOC容器中各个组件的依赖关系赋值.自动装配的优点有: 自动装配可以大大地减少属性和构造器参数的指派. 自动装配也可以在解析对象时更新配置. 自动装配的 ...
- Spring注解开发系列Ⅱ --- 组件注册(下)
1.@Import注册组件 @Import主要功能是通过导入的方式实现把实例加入springIOC容器中, /** * 给容器注册组件 * 1.包扫描+组件标注注解(@Controller,@Serv ...
- Spring注解开发系列Ⅲ --- 生命周期
Bean的生命周期 Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解. 首先看下生命周期图: 再谈生命周期之前有一点需要先明确: S ...
- Spring注解开发系列Ⅳ --- 属性赋值
在Spring框架中,属性的注入我们有多种方式,我们可以通过构造方法注入,可以通过set方法注入,也可以通过p名称空间注入,方式多种多样,对于复杂的数据类型比如对象.数组.List集合.map集合.P ...
随机推荐
- 第二阶段:4.商业需求文档MRD:5.PRD-原型图
页面原型图!
- SQLServer数据库之SqlServer查看表、存储过程、耗时查询、当前进程、开销较大的语句
--查看数据库中表的语句 SELECT s2.dbid , DB_NAME(s2.dbid) AS [数据库名] , --s1.sql_handle , ( , ( ( THEN ( LEN(CONV ...
- ansible批量部署tomcat
1.1 构建目录结构 此操作是安装nginx+mysql+tomcat+db的目录结构,可以参考一下,不错~ mkdir -p /ansible/roles/{nginx,mysql,tomcat,d ...
- 洛谷$P3226\ [HNOI2012]$集合选数 状压$dp$
正解:$dp$ 解题报告: 传送门$QwQ$ 考虑列一个横坐标为比值为2的等比数列,纵坐标为比值为3的等比数列的表格.发现每个数要选就等价于它的上下左右不能选. 于是就是个状压$dp$板子了$QwQ$ ...
- Java日志体系居然这么复杂?——架构篇
本文是一个系列,欢迎关注 日志到底是何方神圣?为什么要使用日志框架? 想必大家都有过使用System.out来进行输出调试,开发开发环境下这样做当然很方便,但是线上这样做就有麻烦了: 系统一直运行,输 ...
- Lyft Level 5 Challenge 2018 - Final Round (Open Div. 2) (前三题题解)
这场比赛好毒瘤哇,看第四题好像是中国人出的,怕不是dllxl出的. 第四道什么鬼,互动题不说,花了四十五分钟看懂题目,都想砸电脑了.然后发现不会,互动题从来没做过. 不过这次新号上蓝名了(我才不告诉你 ...
- spring cloud微服务快速教程之(四)熔断器(Hystrix)及其工具(Dashboard、Turbine)
0-为什么需要熔断器 在分布式系统中,各个服务相互调用相互依赖,如果某个服务挂了,很可能导致其他调用它的一连串服务也挂掉或者在不断等待中耗尽服务器资源,这种现象称之为雪崩效应: 未来防止系统雪崩,熔断 ...
- docker练习-堆栈
介绍 分布式应用程序层次结构的顶部:堆栈. 堆栈是一组相互关联的服务,它们共享依赖关系,并且可以协调和缩放在一起. 单个堆栈能够定义和协调整个应用程序的功能(尽管非常复杂的应用程序可能希望使用多个堆栈 ...
- Mysql备份与恢复(2)---逻辑备份
数据库及时备份可以帮助我们在数据库出现异常宕机时及时的使用备份数据进行恢复工作,将因为数据库宕机产生的影响降低到最小.上一篇针对使用xtrabackup工具进行物理备份和数据恢复做了一个详细讲解,本篇 ...
- 【原创】快速失败机制&失败安全机制
这是why技术的第29篇原创文章 之前在写<这道Java基础题真的有坑!我求求你,认真思考后再回答.>这篇文章时,我在8.1小节提到了快速失败和失败安全机制. 但是我发现当我搜索" ...