异步servlet是servlet3.0开始支持的,对于单次访问来讲,同步的servlet相比异步的servlet在响应时长上并不会带来变化(这也是常见的误区之一),但对于高并发的服务而言异步servlet能增加服务端的吞吐量。本篇来从源码角度上来探究为何说异步servlet能增加服务端的吞吐量的?

首先来个简单的异步servlet的demo



@WebServlet(
    name = "asynchelloServlet",
    urlPatterns = {"/asynchello"},
    asyncSupported = true
)
public class AsyncHelloServlet extends HttpServlet {
    private static final ThreadPoolExecutor executor;     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        AsyncContext ctx = req.startAsync();
        executor.execute(() -> {
            System.out.println("AsyncHello Start->" + LocalDateTime.now());             try {
                PrintWriter writer = ctx.getResponse().getWriter();
                writer.write("asyncHelloWorld");
            } catch (IOException var2) {
                var2.printStackTrace();
            }             ctx.complete();
            System.out.println("AsyncHello End->" + LocalDateTime.now());
        });
    }     static {
        executor = new ThreadPoolExecutor(10, 20, 5000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(100));
    }
}

上面的代码写异步servlet的写法最关键的就是

  • AsyncContext ctx = req.startAsync()
  • ctx.complete()

我们先讲讲下当逻辑进入servlet之前,tomcat经历了哪些步骤:

  • tcp三次握手后
  • Acceptor线程处理 socket accept
  • Acceptor线程处理 注册registered OP_READ到多路复用器
  • ClientPoller线程 监听多路复用器的事件(OP_READ)触发
  • 从tomcat的work线程池取一个工作线程来处理socket[http-nio-8080-exec-xx],下面几个步骤也都是在work线程中进行处理的
  • 因为是http协议所以用Http11Processor来解析协议
  • CoyoteAdapter来适配包装成Request和Response对象
  • 开始走pipeline管道(Valve),最后一个invoke的是把我们的servlet对象包装的StandardWrapperValve管道

接下来就走到我们的servlet,由于是我们是异步的servlet,

1. req.startAsync()

   @Override
    public AsyncContext startAsync(ServletRequest request,
            ServletResponse response) {
        if (!isAsyncSupported()) {
            IllegalStateException ise =
                    new IllegalStateException(sm.getString("request.asyncNotSupported"));
            log.warn(sm.getString("coyoteRequest.noAsync",
                    StringUtils.join(getNonAsyncClassNames())), ise);
            throw ise;
        }         if (asyncContext == null) {
            asyncContext = new AsyncContextImpl(this);
        }
        //修改状态机
        asyncContext.setStarted(getContext(), request, response,
                request==getRequest() && response==getResponse().getResponse());
        asyncContext.setTimeout(getConnector().getAsyncTimeout());         return asyncContext;
    }

从这里开始有2个线程我们要特别关注它们分别做了哪些事情:

  • tomcat的work线程
  • 我们自定义的业务线程

当在tomcat的work线程中调用startAsync(),会创建了一个异步的上下文(AsyncContext),并且异步的上下文(AsyncContext)会设置这个状态机状态为 STARTING, 然后把这个异步上下文放到了我们的自定义线程池中去执行,

对于异步的servlet,有一个专门的状态机来控制:AsyncMachine,如下图

image

那状态机的扭转控制肯定也做针对异步做了什么特殊处理

image

这里是一个Socket状态的切换的处理逻辑,在异步servlet的时候是通过AsyncMachined的状态来连动Socket状态

如上图异步状态机的切换过程为:

DISPATCHED(初始)->STARTING->STARTED->COMPLETING

Socket的状态的切换为:LONG

对于tomcat的work线程而言,servlet调用就结束了! 正常来说,如果是同步servlet的话,request和response会在servlet执行完成后由tomcat释放掉!

image

异步的话 在这个时机request和response肯定不能释放掉,释放那不就没得完了!

虽然Request和Response没有释放,但是这根work线程回到tomcat的线程池中去了(非核心线程的话那就释放)。

2. ctx.complete()

回到我们的业务线程,处理完业务逻辑后,调用ctx.complete()


@Override
public void complete() {
    if (log.isDebugEnabled()) {
        logDebug("complete   ");
    }
    check();
    //更改异步状态机
    request.getCoyoteRequest().action(ActionCode.ASYNC_COMPLETE, null);
}

注意:COMPLETING是在我们的自定义的业务线程改变的!

修改状态会触发 新开一个tomcat工作线程

异步状态状态切换:

COMPLETING->DISPATCHED

Socket状态切换为ASYNC_END

如下图,一次异步的完整过程如下图:

image

总结

研究了整个如何异步的过程,虽然这个状态机的切换挺绕的,会发现在异步servlet中,最大的改变是为了尽快的释放tomcat的work线程,让它有机会请求新accept过来的请求,接受更多的请求,当在自定义线程池中处理好业务逻辑后,在去启动新的tomcat的work线程来处理response,这样不就很好理解了为什么说异步servlet能增加服务端的吞吐量了对吧!

思考:

  1. SpringBoot的@EnableAsync背后是异步servlet吗?​

  2. servlet 3.1的non-blocking I/O 解决了3.0的什么问题?

关注公众号一起学习

异步servlet的原理探究的更多相关文章

  1. 异步Servlet的理解与实践

    AsyncContext理解 Servlet 3.0(JSR315)定义了Servlet/Filter的异步特性规范. 怎么理解"异步Servlet/Filter"及其使用情景? ...

  2. ThreadPoolExcutor 原理探究

    概论 线程池(英语:thread pool):一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间 ...

  3. [原] KVM 虚拟化原理探究(1)— overview

    KVM 虚拟化原理探究- overview 标签(空格分隔): KVM 写在前面的话 本文不介绍kvm和qemu的基本安装操作,希望读者具有一定的KVM实践经验.同时希望借此系列博客,能够对KVM底层 ...

  4. [原] KVM 虚拟化原理探究 —— 目录

    KVM 虚拟化原理探究 -- 目录 标签(空格分隔): KVM KVM 虚拟化原理探究(1)- overview KVM 虚拟化原理探究(2)- QEMU启动过程 KVM 虚拟化原理探究(3)- CP ...

  5. [原] KVM 虚拟化原理探究(6)— 块设备IO虚拟化

    KVM 虚拟化原理探究(6)- 块设备IO虚拟化 标签(空格分隔): KVM [toc] 块设备IO虚拟化简介 上一篇文章讲到了网络IO虚拟化,作为另外一个重要的虚拟化资源,块设备IO的虚拟化也是同样 ...

  6. [原] KVM 虚拟化原理探究(5)— 网络IO虚拟化

    KVM 虚拟化原理探究(5)- 网络IO虚拟化 标签(空格分隔): KVM IO 虚拟化简介 前面的文章介绍了KVM的启动过程,CPU虚拟化,内存虚拟化原理.作为一个完整的风诺依曼计算机系统,必然有输 ...

  7. [原] KVM 虚拟化原理探究(4)— 内存虚拟化

    KVM 虚拟化原理探究(4)- 内存虚拟化 标签(空格分隔): KVM 内存虚拟化简介 前一章介绍了CPU虚拟化的内容,这一章介绍一下KVM的内存虚拟化原理.可以说内存是除了CPU外最重要的组件,Gu ...

  8. [原] KVM 虚拟化原理探究(3)— CPU 虚拟化

    KVM 虚拟化原理探究(3)- CPU 虚拟化 标签(空格分隔): KVM [TOC] CPU 虚拟化简介 上一篇文章笼统的介绍了一个虚拟机的诞生过程,从demo中也可以看到,运行一个虚拟机再也不需要 ...

  9. [原] KVM 虚拟化原理探究(2)— QEMU启动过程

    KVM 虚拟化原理探究- QEMU启动过程 标签(空格分隔): KVM [TOC] 虚拟机启动过程 第一步,获取到kvm句柄 kvmfd = open("/dev/kvm", O_ ...

随机推荐

  1. Rip CD on Ubuntu

    用Mint自带的banshee可以把CD转换为ogg文件,[Media -> Import Media],然后选择Audio CD,但只能转换为ogg格式,好像不能自动获取ID3标签:比较好的方 ...

  2. GET请求与POST请求详解

    一.GET请求 常用于获取服务器数据.常见的发起GET请求的方式有:url.href.src.form. 二.GET请求的格式 例子:index.php?userName=harry&pass ...

  3. 【LeetCode】26. 删除有序数组中的重复项

    26. 删除有序数组中的重复项 知识点:数组:排序:双指针: 题目描述 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度. 不要使用额外的 ...

  4. Longhorn,企业级云原生容器分布式存储 - 高可用

    内容来源于官方 Longhorn 1.1.2 英文技术手册. 系列 Longhorn 是什么? Longhorn 企业级云原生容器分布式存储解决方案设计架构和概念 Longhorn 企业级云原生容器分 ...

  5. js 遍历数组对象求和

    这个通常是求多个商品的总价遇到的情形: [ 0: {id: 1, name: "服务费", price: "1.00"} 1: {id: 2, name: &q ...

  6. 【mysql】mysql简介及高手是如何练成的

    1.什么是mysql  MySQL 是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于Oracle 公司. Mysql 是开源的,可以定制的,采用了GPL 协议,你可以修改源码 ...

  7. 原来:HTTP可以复用TCP连接

    问题 线上的一个项目会和微信服务器有API请求(目的是获取用户的微信信息),但会有偶发的报错: 'Connection aborted.', ConnectionResetError(104, 'Co ...

  8. CentOS 7.3安装Zabbix3.2

    一.ZABBIX概述 Zabbix是一个基于Web界面的分布式系统监控的企业级开源软件.可以监视各种系统与设备的参数,保障服务器及设备的安全运营.   Zabbix的功能和特性: 1.安装与配置简单: ...

  9. 安装配置Linux Squid代理服务器

    1.代理服务器的工作机制 代理服务器的工作机制像生活中的代理商,假设自己的机器为A,想获得的数据由服务器B提供,代理服务器为C,那么连接过程是,A需要B的数据,并直接和C连接:C接受到A的数据请求之后 ...

  10. JS 处理图片平铺问题

    background: url("../../../assets/image/center.png") no-repeat; // 让图片不平铺   overflow: hidde ...