Day169.Servlet3.0_异步请求 -Spring注解驱动

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来进一步增强异步处理的性能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xZXZxZuH-1611130238442)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210120145424830.png)]

每个线程处理完一个请求后才得到释放

测试模拟

@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
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nwAHdmuH-1611130238445)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210120145305763.png)]

2、开启原生的异步模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0bIcvZ4h-1611130238449)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210120151816861.png)]

主线程收到请求后立即返回,并将接收到的请求交给异步线程,并由其完成业务,自己则就空闲,同一时间就会有更多的线程可供使用

@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);
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5dYQpodv-1611130238457)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210120151802918.png)]


二、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监听消息中间件,并获取消息中间件中创建好的订单,并返回出请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s9UhvXjr-1611130238460)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210120160325175.png)]

模拟消息中间件

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响应创建订单后返回请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bqySeaxY-1611130238461)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210120161001026.png)]

localhost:8080//create 来模拟应用2—获取消息中间件中的请求并处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y3xkAjL2-1611130238463)(C:\Users\PePe\AppData\Roaming\Typora\typora-user-images\image-20210120160949069.png)]

感谢尚硅谷!!!

select * from ( SELECT P.PATIENT_ID, P.VISIT_ID, PM.NAME, PM.SEX, P.ADMISSION_DATE_TIME, ( SELECT TOP 1 DP.DEPT_NAME FROM DEPT_DICT DP WHERE DP.DEPT_CODE = P.DEPT_ADMISSION_TO ) AS DEPT_ADM, P.DISCHARGE_DATE_TIME, ( SELECT TOP 1 DP.DEPT_NAME FROM DEPT_DICT DP WHERE DP.DEPT_CODE = P.DEPT_DISCHARGE_FROM ) AS DEPT_DIS, DATEDIFF( DAY, P.ADMISSION_DATE_TIME, P.DISCHARGE_DATE_TIME ) AS INPAT_DAYS, P.DOCTOR_IN_CHARGE --datediff(day, P.ADMISSION_DATE_TIME,P.DISCHARGE_DATE_TIME) as zyts FROM PAT_VISIT P INNER JOIN PAT_MASTER_INDEX PM ON PM.PATIENT_ID = P.PATIENT_ID WHERE select * from ( SELECT P.PATIENT_ID, P.VISIT_ID, PM.NAME, PM.SEX, P.ADMISSION_DATE_TIME, ( SELECT TOP 1 DP.DEPT_NAME FROM DEPT_DICT DP WHERE DP.DEPT_CODE = P.DEPT_ADMISSION_TO ) AS DEPT_ADM, P.DISCHARGE_DATE_TIME, ( SELECT TOP 1 DP.DEPT_NAME FROM DEPT_DICT DP WHERE DP.DEPT_CODE = P.DEPT_DISCHARGE_FROM ) AS DEPT_DIS, DATEDIFF( DAY, P.ADMISSION_DATE_TIME, P.DISCHARGE_DATE_TIME ) AS INPAT_DAYS, P.DOCTOR_IN_CHARGE --datediff(day, P.ADMISSION_DATE_TIME,P.DISCHARGE_DATE_TIME) as zyts FROM PAT_VISIT P INNER JOIN PAT_MASTER_INDEX PM ON PM.PATIENT_ID = P.PATIENT_ID WHERE P.DISCHARGE_DATE_TIME >= '2016-01-01' AND P.DISCHARGE_DATE_TIME < = '2023-07-10' AND DATEDIFF( DAY,P.ADMISSION_DATE_TIME, P.DISCHARGE_DATE_TIME ) >= 30 ) t order by inpat_days desc P.DISCHARGE_DATE_TIME >= '2016-01-01' AND P.DISCHARGE_DATE_TIME < = '2023-07-10' AND DATEDIFF( DAY,P.ADMISSION_DATE_TIME, P.DISCHARGE_DATE_TIME ) >= 30 ) t order by inpat_days desc 帮我把这段sql优化一下
最新发布
07-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿昌喜欢吃黄桃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值