使用Callable以及DeferredResult来异步处理Restful请求

时间:2024-03-28 13:22:10

异步处理Restful请求有两个方案
方案1:使用Callable异步处理Rest服务
方案2:使用DeferredResult异步处理Rest服务

Callable异步处理

同步处理的问题

tomcat thread有数量限制的,再有请求进来,就没办法处理了
使用Callable以及DeferredResult来异步处理Restful请求
异步处理

tomcat主线程调用一个副线程,副线程去执行具体逻辑,当副线程整个的处理逻辑完成之后,主线程再过来把结果返回回去,在副线程处理整个业务逻辑的过程中,主线程是可以空闲出来,可以处理其他请求的,使用这种方式服务器的吞吐量会得到很大的提升

代码参考:https://gitee.com/constfafa/spring_springboot_learning/tree/master/deferresult-demo
cn.bellychang.controller.AsyncController#orderCallable

执行过程
使用Callable以及DeferredResult来异步处理Restful请求
可以看到callable-async-thread-1副线程返回后,仍回到dispatchServlet中,由另外的tomcat线程返回结果

Callable这种异步方式有局限性
在这种方式下,Callable必须是要由主线程调起的,在真正的企业级开发中,场景要比这样的方式复杂。

DeferredResult异步处理

下面看一个复杂一些的例子
使用Callable以及DeferredResult来异步处理Restful请求
真正处理下单请求的应用为应用1,真正处理下单逻辑的应用为应用2
应用1的tomcat线程1接收到下单请求之后,会把请求放到待下单消息队列里面,应用2来监听这个消息队列,由应用2的order-process-thread处理下单逻辑,当应用2处理完成之后,将结果处理结果放回到订单处理结果的消息队列中。
在应用1中有另外一个线程,这里是queue-listener-thread在监听订单处理结果的消息队列,去返回一个http响应。
如果在指定的时间内B未给A推送数据,则返回超时。(给人一种异步处理业务,但是却同步返回的感觉)

应用1中的线程1(tomcat线程)和线程2(queue-listener-thread)是彼此不知道的,在这种场景下,是无法使用Callable方式

代码参考:https://gitee.com/constfafa/spring_springboot_learning/tree/master/deferresult-demo
cn.bellychang.controller.AsyncController#orderDeferredResult

执行过程:
使用Callable以及DeferredResult来异步处理Restful请求
DeferredResultHolder:要在线程1和线程2之间传递DeferedResult对象,这样其才能在线程1把消息发出去之后,用线程1生成的DeferredResult在线程2中把线程2监听到的处理结果返回回去。

实际上我们有四个线程

  1. 应用1的tomcat主线程http-nio-8080-exec-4来接收http请求
  2. 应用2的order-process-thread线程来实现真正的下单逻辑
  3. 应用1的queue-listener-thread来监听处理结果并发出http响应
  4. 应用1的tomcat主线程http-nio-8080-exec-5来返回结果
    应用1的http-nio-8080-exec-4线程与queue-listener-thread线程之间会通过deferredResult来进行信息的交互

当一个请求到达API接口,如果该API接口的return返回值是DeferredResult,在没有超时或者DeferredResult对象设置setResult时,接口不会返回,但是Servlet容器即tomcat线程(http-nio-8080-exec-4)会结束,DeferredResult另起线程来进行结果处理(即这种操作提升了服务短时间的吞吐能力),并setResult,如此以来这个请求不会占用服务连接池太久,如果超时或设置setResult,接口会立即返回。