异步接口的重要性
我们都知道同步调用会阻塞线程,将耗时的工作放到线程池中工作是常识,这对于servlet容器同样适用,每个Servlet容器的处理能力有限,他能同时处理的连接数是有限的,如果在我们的Servlet方法中一直占用着Servlet连接,那么容器的并发性能将大打折扣,所以我们要求后端的Servlet响应时间不能太长。
除了加快servlet的处理速度,我们也可以将其传统的同步接口模式改为异步接口模式,这样可以保证主线程的资源可以迅速被释放,提高程序处理能力,现在微服务模式大行其道,服务众多,调用链复杂,一旦请求超时而未做熔断处理的话将会对我们的系统造成很大的影响。
异步Servlet
Servlet在3.0版本中加入了对异步请求的支持,将耗时任务放到额外的线程中执行,使得每次请求Servlet的主线程可以快速返回。它的原理是通过request.startAsync()开启一个异步的ServletRquest得到AsyncContext,通过AsyncContext执行任务,此时的Servlet线程可以回收到线程池,但是resonse将会保持打开状态,然后在新的线程中完成对请求的处理并返回结果给客户端。由于现在开发基本都不会单纯的写Servlet,大家都是使用Spring,所以这个代码我就不贴出来了,因为搭建这个好麻烦(还是喜欢SpringBoot的快速演示,哈哈哈),但是知道他的实现原理觉得还是很有必要的,SpringMVC的异步接口都是基于它实现的。
SpringMVC对异步接口的支持
在了解Spring对异步Servlet的支持之前,先来看一下传统方式的Controller接口开发
传统方式这是一个最简单的controller,我们访问http://localhost:8080/n,就可以看到返回结果了,这时假如在doSomething中做了大量耗时的操作,那么该Servlet线程将会一直被占用,直到结果返回,如果请求量增加,导致Servlet线程用完,那么将会停止服务,这不是我们所希望的。
接下来我们来看如何使用Spring实现异步Controller。
1:返回Callable
Callable在多线程开发中大家都不陌生,它提前返回一个Future对象,再通过get方法获取具体的数据。在Controller中我们也可以直接返回Callable,如图。
我们直接返回一个Callable对象,并模拟耗时操作在callable线程中让他睡个3秒,这次访问http://localhost:8080/hello2,等待3秒过后就可以看到如下结果
2.返回DeferredResult
这里我们返回一个DeferredResult对象,新开一个线程模拟处理耗时任务等待3秒,然后调用DeferredResult的setResult方法为其设置返回值,访问http://localhost:8080/hello,等待3秒过后就可以看到
这样看起来和Callable方式没啥不同,但是他还可以做到更厉害的事情
这里写了两个处理器,第一个开启DeferredResult,第二个设置DeferredResult的值,这时我们先访问http://localhost:8080/startDef,我们会看到浏览器一直在转圈等待,这时再访问 /setDef
在set方法中设置的值返回到了第一次请求中,同时可以看到第一次请求结束了等待,请求完成。
执行过程
上面的请求都会等待3秒,那么是不是意味着Servlet线程是在3秒后释放的呢?和原始方式没有区别?
接下来分别来详细说下上面两种方式的执行流程:
Callable方式:Controller返回Callable;Spring调用request.startAsync()并提交Callable给其他线程执行;DispatchServlet和所有的filter退出Servlet线程(注意,这里Servlet线程已经结束),但是保持response打开;Callable执行的到结果,Spring将结果返回给Spring容器;再次调用DispatchServlet,处理返回值。
DeferredResult方式:Controller返回DeferredResult并将其保存在内存队列中,Spring调用request.startAsync();DispatchServlet和所有的filter退出Servlet线程,但是保持response打开;程序设置DeferredResult的result,Spring将结果返回给Spring容器;再次调用DispatchServlet,处理返回值。
这里要说明一点的是最后再次调用DispatchServlet时只是单纯的返回结果,而不进行调用,可以看到从执行过程来看二者并无太大区别,但是DeferredResult方式更加灵活,Callable方式更加简单。
最后
最后简单说一下WebFlux,它是Spring推出的响应式编程框架,它并不基于Servlet,所以跟上面的实现没有任何关系,它在设计之初就是异步的,属于“纯天然异步”,很早就包含在Spring的项目中了,使用它的另一个好处就是可以使用函数式编程风格,并且Spring认为他是未来的趋势,坏处就是我本身对他接触的比较少.......,所以这里就不详细说了,以后有机会再回来写,感兴趣的同学可以移步官网WebFlux
网友评论