在上一篇文章中我们主要介绍 tomcat nio 中异步的开启,主要包括以下的总结:
在这里我们主要介绍任务的运行,包括异步的运行以及运行的流程。
异步的运行:
对于异步的运行,我们一般有如下代码:
public void testAsync(HttpServletRequest request, HttpServletResponse response) {
try{
AsyncContext context = request.startAsync();
context.start(new Runnable(){
@Override
public void run() {
/**
* Do you own logic here in business thread and set final result into response
*/
//Complate asyn thread
context.complete();
}});
}catch(Exception e){
//Handle exception here
}
}
AsyncContext 对象的 start() 方法开启了我们的异步运行,该方法接受runnable 类型的对象,在异步线程中运行我们的逻辑。
运行异步的流程:
对于 AsyncContext.start() 的源码调用序列图如下:
从 AsyncContext 的 star() 方法开始,一直调用到 SocketWrapperBase 对象实例的 execute() 方法,这里我们分析 execute() 核心方法:
上述方法首先通过 endpoint.getExecutor() 调用得到 tomcat 的 io 线程池。
将异步任务委托到 tomcat io 线程池中运行。
细心的你就会发现,在 tomcat 原生异步实现的 API 中,任务是占用了 io 线程的。我们并不建议这样做,因为 io 线程是 servlet 的运行线程,所以当大量异步任务开启的时候势必会占用 io 线程池中的大量资源。从而影响 servlet 请求的运行,进而影响了服务器的吞吐率。
所以在这种情况下我们建议引入业务线程池,将异步任务在业务线程池中运行,得到结果,设置响应,结束异步。这样释放 io 线程,避免影响服务器吞吐率,示例代码如下:
当然关于业务线程池的配置,例如核心线程数大小,最大线程数大小,任务队列大小,线程拒绝策略,是否预启动核心线程,非核心线程的空闲回收时间等等要结合实际场景做设置,我们不在这里讨论。
所以综上总结对于 tomcat 异步原生 API 实现中, AsyncContext.start() 方法会把异步任务交由 tomcat io 线程池运行,这样在大量启动异步任务的时候可能会过度占用 io 线程池,从而导致服务器吞吐率下降。所以一般建议引入业务线程池,根据场景设置好业务线程池的参数,把异步任务的执行,响应结果的设置,异步任务的结束等交由业务线程池运行。从而释放 io 线程池,避免降低吞吐率。
目前先写到这里,下一篇文章里我们继续介绍 tomcat io 中的异步结束。