异步servlet的原理探究
异步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三次握手后 56c
- 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()) {   ad8 ; 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 564 的时候是通过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()) { &nbs ad8 p; 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能增加服务端的吞吐量了对吧!
思考:
-
SpringBoot的@EnableAsync背后是异步servlet吗?
-
servlet 3.1的non-blocking I/O 解决了3.0的什么问题?
关注公众号一起学习
- [置顶] SpringBoot 整合SpringMvc 原理探究(DispatchServlet添加流程)
- Servlet基础_0400_Session 原理探究
- FormData兼容IE10 360及DWR的异步上传原理
- Servlet——Session(3)之实现原理的深入讨论
- AJAX的原理―如何做到异步和局部刷新【实现代码】
- Servlet过滤器原理分析
- Java之戳中痛点 - (3)三目运算符的两个操作数类型尽量一致 Java之戳中痛点 - (4)i++ 和 ++i 探究原理 Java之戳中痛点 - (1)易变业务使用脚本语言编写 Java之戳中痛点 - (2)取余用偶判断,不要用奇判断 (5)switch语句break不能忘以及default不同位置的用法 Java之戳中痛点 - (7)善用Java整型缓存池
- ajax--异步原理 readyState
- Linux字符设备原理探究--2(应用层调用)
- 深入学习----Servlet工作原理解析
- Tomcat 容器与servlet的交互原理
- MyBatis拦截器原理探究
- solr replication原理探究
- Android原理探究:Activity任务栈Task的简单探讨
- [Java] Servlet工作原理之二:Session与Cookie
- 由浅入深探究mysql索引结构原理、性能分析与优化
- ajax异步文件上传,servlet处理,含demo
- MVCC原理探究及MySQL源码实现分析
- servlet3.0新特性Servlet3.0引入的若干重要新特性,包括异步处理、新增的注解支持、可插性支持等等,为读者顺利向新版本过渡扫清障
- Servlet 重定向原理