select的异步请求

1、什么是servlet异步请求

Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:

(1)、Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;

(2)、调用业务接口的某些方法,以完成业务处理;

(3)、根据处理的结果提交响应,Servlet 线程结束。

其中第二步处理业务逻辑时候很可以碰到比较耗时的任务,此时servlet主线程会阻塞等待完成业务处理,对于并发比较大的请求可能会产生性能瓶颈,则servlet3.0之后再此处做了调整,引入了异步的概念。

(1)、Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;

    (2)、调用业务接口的某些方法过程中request.startAsync()请求,获取一个AsyncContext

    (3)、紧接着servlet线程退出(回收到线程池),但是响应response对象仍旧保持打开状态,新增线程会使用AsyncContext处理并响应结果。

     (4)、AsyncContext处理完成触发某些监听通知结果

2、Servlet异步请求示例

       2.1、示例准备

         本示例采用web.xml配置的形式,模拟场景为:笔者所在的it公司每周的工作内容,首先研发总监分配给产品、研发、测试相关的任务,布置完任务就出差(模拟请求响应),余下的各个小组进行自己任务操作(模拟的耗时操作),最终出周报完成任务(异步任务处理完成的通知)

   git地址:https://github.com/liushangzaibeijing/spsm.git  分支:dev_async

       2.2、实现自定义的Servlet

 
  1. /**

  2. * @ClassName AsyncServlet

  3. * @Desc 自定义异步Servlet处理器

  4. * @Author xieqx

  5. * @Date 2020/12/9 15:38

  6. **/

  7. //通过注解的形式开始异步

  8. @WebServlet(urlPatterns = "*.async",asyncSupported = true)

  9. public class AsyncServlet extends HttpServlet {

  10. @Override

  11. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

  12. doPost(req,resp);

  13. }

  14. @Override

  15. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

  16. //开启异步支持

  17. //异步管理上下文

  18. resp.setCharacterEncoding("GBK");

  19. PrintWriter writer = resp.getWriter();

  20. writer.println("周工作任务布置开始");

  21. AsyncContext asyncContext = req.startAsync();

  22. asyncContext.start(new WeekTask(asyncContext));

  23. //添加监听器 处理完成监听

  24. asyncContext.addListener(new AsyncListener() {

  25. @Override

  26. public void onComplete(AsyncEvent asyncEvent) throws IOException {

  27. System.out.println("工作在"+new Date()+"处理完成");

  28. }

  29. @Override

  30. public void onTimeout(AsyncEvent asyncEvent) throws IOException {

  31. System.out.println("工作在"+new Date()+"处理超时");

  32. }

  33. @Override

  34. public void onError(AsyncEvent asyncEvent) throws IOException {

  35. System.out.println("工作在"+new Date()+"处理出错");

  36. }

  37. @Override

  38. public void onStartAsync(AsyncEvent asyncEvent) throws IOException {

  39. System.out.println("工作在"+new Date()+"处理开始");

  40. }

  41. });

  42. writer.println("周工作任务布置完成");

  43. writer.flush();

  44. }

  45. }

  开启异步支持(默认异步支持不开启)有两种方式:

  •    使用注解

20201212120848305.png

  • web.xml配置

 
  1.  
  2. <servlet>

  3. <servlet-name>asyncServlet</servlet-name>

  4. <servlet-class>com.xiu.async.servlet.AsyncServlet</servlet-class>

  5. <!-- 开启servlet的异步请求操作 -->

  6. <async-supported>true</async-supported>

  7. </servlet>

  8. <servlet-mapping>

  9. <servlet-name>asyncServlet</servlet-name>

  10. <url-pattern>*.async</url-pattern>

  11. </servlet-mapping>

上述代码中通过request.startAsync()启动异步处理 返回一个异步上下文对象AsyncContext最终是使用该上下文对象来进行异步业务逻辑处理,其中有两个核心方法 

asyncContext.start(new WeekTask(asyncContext));  添加一个异步任务该任务是一个Runnable线程接口,这里就清晰了其实是servlet线程将处理任务交给另一个子线程,servlet直接返回从而达到提高系统吞吐量的作用。

对于异步请求可以我们需要获取其中的结果,所有这里提供了监听器模式添加事件监听AsyncListener

onComplete
异步请求处理完成触发 前提示需要调用 asyncContext.complete()方法(因为程序也不知道什么时候任务算是调用完毕了)
onTimeout
异步请求处理超时触发,一般来说采用异步请求的任务都是比较耗时的任务,所以需要修改servlet默认的超时时间(修改的长一点) 
onError
异步处理错误的时候触发
onStartAsync
异步处理开始的时候触发即为request.startAsync(),因为添加监听器在startAsync()方法后,所以第一个启动是无法触发该监听的

 

这里异步处理只是简单的打印了相关日志,不过真实的业务场景中可以写复杂的业务处理逻辑。

 

3.3、异步任务

   这里提供相关的异步操作是实现runnable的线程实现类,同时这里提供了相关Job,PmJob(产品任务),RDJob(研发任务),TestJob(测试任务),每个任务模拟了10秒的耗时任务。

 
  1. **

  2. * @ClassName WeekTask

  3. * @Desc 每周任务

  4. * @Author xieqx

  5. * @Date 2020/12/10 9:36

  6. **/

  7. public class WeekTask implements Runnable {

  8.  
  9. private List<Job> jobs = null;

  10.  
  11. private AsyncContext asyncContext = null;

  12. //这里初始化产品任务PmJob、研发任务RDJob 测试任务TestJob

  13. public WeekTask(AsyncContext asyncContext) {

  14. this.asyncContext = asyncContext;

  15. jobs = new ArrayList<>();

  16. PmJob pmJob = new PmJob();

  17. RDJob rdJob = new RDJob();

  18. TestJob testJob = new TestJob();

  19. jobs.add(pmJob);

  20. jobs.add(rdJob);

  21. jobs.add(testJob);

  22. }

  23.  
  24. @Override

  25. public void run() {

  26. for(Job job:jobs){

  27. job.execute();

  28. }

  29. System.out.println("周任务工作完成");

  30. //job执行完成后通知

  31. asyncContext.complete();

  32. }

  33. }

  PmJob

 
  1. /**

  2. * @ClassName PmTask

  3. * @Desc 产品经理任务

  4. * @Author xieqx

  5. * @Date 2020/12/9 16:03

  6. **/

  7. public class PmJob implements Job {

  8. @Override

  9. public void execute() {

  10. System.out.println("产品经理开评审会议");

  11. try {

  12. Thread.sleep(10);

  13. System.out.println("模拟需求评审会议...");

  14. } catch (InterruptedException e) {

  15. e.printStackTrace();

  16. }

  17. }

  18. }

RDJob

 
  1. /**

  2. * @ClassName PmTask

  3. * @Desc 研发任务

  4. * @Author xieqx

  5. * @Date 2020/12/9 16:03

  6. **/

  7. public class RDJob implements Job {

  8. @Override

  9. public void execute() {

  10. System.out.println("程序猿开始开发");

  11. try {

  12. Thread.sleep(10);

  13. System.out.println("程序猿哼哧哼哧干活中...");

  14. } catch (InterruptedException e) {

  15. e.printStackTrace();

  16. }

  17. }

  18. }

TestJob

 
  1. /**

  2. * @ClassName TestJob

  3. * @Desc 测试任务

  4. * @Author xieqx

  5. * @Date 2020/12/9 16:03

  6. **/

  7. public class TestJob implements Job {

  8. @Override

  9. public void execute() {

  10. System.out.println("测试开始测试");

  11. try {

  12. Thread.sleep(10);

  13. System.out.println("测试用例测试...");

  14. } catch (InterruptedException e) {

  15. e.printStackTrace();

  16. }

  17. }

  18. }

 

3.4、测试场景

   请求立马响应,但是异步任务在后面处理

  20201212130111601.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值