在上一篇文章中我们主要介绍 tomcat nio 中异步的开启,主要包括以下的总结:
开启异步的时候不会执行 start 事件函数的,因为此时构造的异步上下文对象的源码中并没有设置事件监听器(可能 tomcat 异步实现者并没有考虑这个问题)。
请求的异步状态由 AsyncStateMachine 状态机保持维护,并且该状态机属于每个 socket 请求所关联的处理类实例 Http11Processor 对象。
状态机初值为 AsyncState.DISPATCHED,开启异步改变其状态为 AsyncState.STARTING。
在这里我们主要介绍任务的运行,包括异步的运行以及运行的流程。
异步的运行:
对于异步的运行,我们一般有如下代码:
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() 核心方法:
public void execute(Runnable runnable) { Executor executor = endpoint.getExecutor(); if (!endpoint.isRunning() || executor == null) { throw new RejectedExecutionException(); } executor.execute(runnable); }
上述方法首先通过 endpoint.getExecutor() 调用得到 tomcat 的 io 线程池。
将异步任务委托到 tomcat io 线程池中运行。
细心的你就会发现,在 tomcat 原生异步实现的 API 中,任务是占用了 io 线程的。我们并不建议这样做,因为 io 线程是 servlet 的运行线程,所以当大量异步任务开启的时候势必会占用 io 线程池中的大量资源。从而影响 servlet 请求的运行,进而影响了服务器的吞吐率。
所以在这种情况下我们建议引入业务线程池,将异步任务在业务线程池中运行,得到结果,设置响应,结束异步。这样释放 io 线程,避免影响服务器吞吐率,示例代码如下:
public void testAsync(HttpServletRequest request, HttpServletResponse response) { try{ AsyncContext context = request.startAsync(); ThreadPool pool = ThreadPool.getThreadPool(); pool.execute(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 ex){ //Handle exception here } }
当然关于业务线程池的配置,例如核心线程数大小,最大线程数大小,任务队列大小,线程拒绝策略,是否预启动核心线程,非核心线程的空闲回收时间等等要结合实际场景做设置,我们不在这里讨论。
所以综上总结对于 tomcat 异步原生 API 实现中, AsyncContext.start() 方法会把异步任务交由 tomcat io 线程池运行,这样在大量启动异步任务的时候可能会过度占用 io 线程池,从而导致服务器吞吐率下降。所以一般建议引入业务线程池,根据场景设置好业务线程池的参数,把异步任务的执行,响应结果的设置,异步任务的结束等交由业务线程池运行。从而释放 io 线程池,避免降低吞吐率。
目前先写到这里,下一篇文章里我们继续介绍 tomcat io 中的异步结束。