Spring / Spring boot 异步任务编程 WebAsyncTask
今天一起学习下如何在 Spring 中进行异步编程。我们都知道,web 服务器处理请求request
的线程是从线程池中获取的,这也不难解释,因为当 web 请求并发数非常大时,如何一个请求进来就创建一条处理线程,由于创建线程和线程上下文切换的开销是比较大的,web 服务器最终将面临崩溃。另外,web 服务器创建的处理线程从头到尾默认是同步执行的,也就是说,假如处理线程 A 负责处理请求 B,那么当 B 没有return
之前,处理线程 A 是不可以脱身去处理别的请求的,这将极大限制了 web 服务器的并发处理能力。
因此线程池解决了线程可循环利用的问题,那同步处理请求怎么去解决呢?答案是异步处理。什么是异步处理呢?异步处理主要是让上面的 B 请求处理完成之前,能够将 A 线程空闲出来继续去处理别的请求。那么我们可以这样做,在 A 线程内部重新开启一个线程 C 去执行任务,让 A 直接返回给 web 服务器,继续接受新进来的请求。
在开始下面的讲解之前,我在这里先区别下两个概念:
1、处理线程
2、异步线程
spring 中提供了对异步任务的支持,采用WebAsyncTask
类即可实现异步任务,同时我们也可以对异步任务设置相应的回调处理,如当任务超时、抛出异常怎么处理等。异步任务通常非常实用,比如我们想让一个可能会处理很长时间的操作交给异步线程去处理,又或者当一笔订单支付完成之后,开启异步任务查询订单的支付结果。
一、正常异步任务
为了演示方便,异步任务的执行采用Thread.sleep(long)
模拟,现在假设用户请求以下接口 :
http://localhost:7000/demo/getUserWithNoThing.json
异步任务接口定义如下:
控制台打印如下:
浏览器结果如下:
二、抛异常异步任务
接口调用 : http://localhost:7000/demo/getUserWithError.json
控制台输出如下:
当然你也可以对上面做一些异常处理,不至于在用户看来显得不友好,关于异常处理,可以查看我的另一篇博文 Spring boot/Spring 统一错误处理方案的使用
浏览器输出结果:
三、超时异步任务
接口调用 : http://localhost:7000/demo/getUserWithTimeOut.json
控制台执行结果:
浏览器执行结果:
四、线程池异步任务
上面的三种情况中的异步任务默认不是采用线程池机制进行管理的,也就是说,一个请求进来,虽然释放了处理线程,但是系统依旧会为每个请求创建一个异步任务线程,也就是上面我们看到的MvcAsync
开头的异步任务线程,那这样不行啊,开销特别大呀!所以我们可以采用线程池进行管理,直接在WebAsyncTask
类构造器传入一个ThreadPoolTaskExecutor
对象实例即可。
下面我们先看看,当对上面第一种情况执行并发请求时会出现什么情况 (此处模拟对http://localhost:7000/demo/getUserWithNoThing.json
进行并发调用):
控制台输出如下:
由于没有加入线程池,所以 100 个请求将开启 100 个异步任务线程,开销特别大,不推荐。
下面是采用线程池的实现 :
调用接口 : http://localhost:7000/demo/getUserWithExecutor.json
线程池定义如下:
对上面进行并发测试,可以得出下面结果 :
采用线程池可以节约服务器资源,优化服务器处理能力,要记得常用哟!
评论