Tomcat NIO(20)-异步任务运行

上一篇文章中我们主要介绍 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 中的异步结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值