脚本宝典收集整理的这篇文章主要介绍了异步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的写法最关键的就是
我们先讲讲下当逻辑进入servlet之前,tomcat经历了哪些步骤:
接下来就走到我们的servlet,由于是我们是异步的servlet,
@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线程中调用startAsync(),会创建了一个异步的上下文(AsyncContext),并且异步的上下文(AsyncContext)会设置这个状态机状态为 STARTING, 然后把这个异步上下文放到了我们的自定义线程池中去执行,
对于异步的servlet,有一个专门的状态机来控制:AsyncMachine,如下图
那状态机的扭转控制肯定也做针对异步做了什么特殊处理
这里是一个Socket状态的切换的处理逻辑,在异步servlet的时候是通过AsyncMachined的状态来连动Socket状态
如上图异步状态机的切换过程为:
DISPATCHED(初始)->STARTING->STARTED->COMPLETING
Socket的状态的切换为:LONG
对于tomcat的work线程而言,servlet调用就结束了! 正常来说,如果是同步servlet的话,request和response会在servlet执行完成后由tomcat释放掉!
异步的话 在这个时机request和response肯定不能释放掉,释放那不就没得完了!
虽然Request和Response没有释放,但是这根work线程回到tomcat的线程池中去了(非核心线程的话那就释放)。
回到我们的业务线程,处理完业务逻辑后,调用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
如下图,一次异步的完整过程如下图:
研究了整个如何异步的过程,虽然这个状态机的切换挺绕的,会发现在异步servlet中,最大的改变是为了尽快的释放tomcat的work线程,让它有机会请求新accept过来的请求,接受更多的请求,当在自定义线程池中处理好业务逻辑后,在去启动新的tomcat的work线程来处理response,这样不就很好理解了为什么说异步servlet能增加服务端的吞吐量了对吧!
思考:
SpringBoot的@EnableAsync背后是异步servlet吗?
servlet 3.1的non-blocking I/O 解决了3.0的什么问题?
关注公众号一起学习
以上是脚本宝典为你收集整理的异步servlet的原理探究全部内容,希望文章能够帮你解决异步servlet的原理探究所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。