当前位置 博文首页 > blackball1998的博客:请求异步处理
当我们使用Spring Boot进行web开发时,默认使用Tomcat作为Servlet的容器,而Tomcat的线程池大小默认为200,如果我们的处理流程很长,就可能因为请求过多而造成线程池里的线程占满,这时候可以使用异步线程
在Spring MVC中,如果我们的请求处理方法返回一个Callable接口,则会开启异步线程功能,主线程接收到请求,然后把处理交给副线程处理,这样主线程就可以空闲出来去处理别的请求
当副线程处理完成后,再获取到副线程的处理结果,然后返回给前端
@RestController
public class MyController {
@RequestMapping("/test")
public Callable<String> test() {
System.out.println("主线程" + Thread.currentThread());
return () -> {
System.out.println("副线程" + Thread.currentThread());
return "SUCCESS";
};
}
}
发送一个测试请求,可以获取到副线程中的返回值
在后台也可以看到打印的线程信息,接收请求的线程和处理的线程是两个不一样的线程
那么接受请求和处理的请求是由两个线程完成的,在返回数据的时候又是怎么能得到处理线程的数据,并且返回给相对应的请求呢?在Spring MVC的官方文档中有答案
Callable
到TaskExecutor
一个单独的线程中进行处理DispatcherServlet
和所有过滤器退出Servlet容器线程,但响应保持打开状态DispatcherServlet
,并且返回副线程的处理结果如果我们添加一个拦截器,就会发现拦截器拦截了两次请求
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行前置处理方法");
System.out.println("拦截到的请求" + request.getRequestURI());
return true;
}
}
这是因为再次调用DispatcherServlet
并恢复原来的响应时,又被拦截器拦截到了
使用DeferredResult
类,也可以实现异步处理,但是效果不同,它可以让处理请求的线程一直等待,直到DeferredResult
类在另外的某处地方被处理并设置了返回值,这时候才将返回值返回给前端
我们用一个队列来保存DeferredResult
对象,模拟请求的延时处理,在/test请求中产生一个DeferredResult
对象,而在/handle请求中处理这个对象,这时候/test请求才会得到响应
@RestController
public class MyController {
private static final Queue<DeferredResult<String>> queue = new LinkedList<>();
@RequestMapping("/test")
public DeferredResult<String> test() {
DeferredResult<String> result = new DeferredResult<>();
queue.add(result);
return result;
}
@RequestMapping("/handle")
public String handle() {
DeferredResult<String> result = queue.poll();
result.setResult("done");
return "success";
}
}
首先我们发送/test请求,浏览器一直等待响应
这时候我们再发送/handle请求,可以发现此前的/test请求拿到了返回值
我们还可以在构造DeferredResult
对象的时候,使用构造方法设置处理的超时时间和超时时返回的结果
@RestController
public class MyController {
@RequestMapping("/test")
public DeferredResult<String> test() {
DeferredResult<String> result = new DeferredResult<>(3000L, "fail");
queue.add(result);
return result;
}
}
使用异步拦截器,可以在请求方法异步执行时执行拦截方法
使用方法只需要创建自定义拦截器并实现AsyncHandlerInterceptor
接口,重写afterConcurrentHandlingStarted
方法,方法中的逻辑就会在请求方法异步执行时执行
最后需要在Spring MVC的web配置中注册拦截器,方法与普通的拦截器一样
public class MyInterceptor implements AsyncHandlerInterceptor {
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行异步拦截器");
}
}