Tomcat源码学习第4篇 - Servlet请求分析

前段时间家里有事忙,停更了好长一段时间,这里跟等待更新的小伙伴们说一声抱歉,没能提早说明一下,让小伙伴们等了这么久,真的很差意思!

前面说完了Tomcat的初始化和启动步骤,那么接下来就要进入重头戏了!在本篇文章中,我会跟前面同样,经过图文的方式来带着小伙伴们了解一个 Servlet是如何被tomcat处理的,具体的处理链路都有哪些。java

1、请求分析

在《Tomcat源码学习第2篇》中备注了各个组件的说明。git

当一个servlet请求到来的时候,首先通过的是connector组件,它是用来接收请求的。github

该组件接收到请求以后,会把相关请求进行封装,而后传递到engine组件中。缓存

紧跟着,engine组件会锁定对应的hostcontext以及wrapper,一层层的传递下去,找到最终处理请求的servlet实例。tomcat

请求链路

2、深刻探索

不知道你们还有没有印象,在前面的文章中,咱们在NioEndpoint类中,启动Accepter线程的入口处上方还有着一个线程组在启动运行,然而却没有讲解该线程是用来干吗的~app

NioEndpoint.startInternal()

NioEndpoint.startInternal()

点击跳转到该类过来,咱们能够看到他实现了Runnable接口,那么咱们直接查看他的run()方法,看看它的运行逻辑。socket

Poller.run()

Poller

经过注释咱们能够知道,该线程主要用于轮询已链接的套接字,检查是否触发了事件,并在事件发生时将关联的套接字移交给对应的处理器。在源码中咱们能够看到keyCount变量记录着待处理请求数,提供给后面作相应判断。ide

Poller.run()

继续往下走,经过keyCount判断是否有请求须要进行处理,须要的话则经过selector.selectedKeys()拿到须要被处理的channel集合,进行循环处理。在while循环中咱们看到,全部就绪的通道都调用的是processKey(sk, socketWrapper)方法进行处理。post

image-20210503200131828

点击跳转过来该方法,在这里能够看到他对该sk作了读写判断,既然是请求进来,那确定是作读操做,咱们先进读相关的方法看一下。学习

NioEndpoint.processKey()

NioEndpoint.processKey()

进来以后咱们能够看到它首先在缓存池中尝试去获取一个处理线程,当缓存池中没有线程时,就建立一个新的线程,若是有的话就直接使用。

AbstractEndpoint.processSocket()

AbstractEndpoint.processSocket()

既然是线程了,那么咱们就关心线程的核心方法便可。点击SocketProcessorBase跳转查看run()方法。

SocketProcessorBase.run()

SocketProcessorBase.run()

doRun()处打上断点,单击下一步,跳转到NioEndpoint.doRun()方法中。Poller线程移交到这边的线程进行处理,在该线程中须要获得当前的socket,作进一步的处理。

NioEndpoint.doRun()

image-20210503212817648

进入该方法以后,咱们能够看到它首先对wrapper进行判断,不为空再取出socket,而后尝试着在connections中去获取对应的processor,若是获取不到,再尝试获取已经处理过链接,可是还没有销毁的processor中去获取,还获取不到才进行建立。这样能够避免频繁的建立和销毁对象。

AbstractProtocol.process()

AbstractProtocol.process()

AbstractProtocol.process()

获得processor以后,调用process方法对报文进行解析。

AbstractProtocol.process()

进入该方法以后,咱们能够看到这里面是对socketEvent的状态进行判断,咱们当前请求主要是读状态,在此处打上断点,跳到该方法进来看一下。

AbstractProcessorLight.process()

AbstractProcessorLight.process()

这里咱们能够看到是进入到了 http11类中,在该类里面对报文进行解析,封装原生的requestresponse对象。这里的response由于咱们尚未到返回的步骤,因此只是作个初步的参数设置。后续要传入Adapter进行下一步操做。

Http11Processor.service()

Http11Processor.service()

Http11Processor.service()

Http11Processor.service()

在这里对原生的requestresponse进行转换,获得HttpServletRequestHttpServletResponse。而后根据请求信息找到可以处理当前请求的hostcontextwrapper

CoyoteAdapter.service()

CoyoteAdapter.service()

在这方法能够看到它会经过getMapper()方法去匹配可以处理当前请求的 host,context,wrapper。到这里可能有的小伙伴会奇怪,为何是从mapper中去匹配呢?这个问题留给大家去探索一下,等下篇再给大家解答。

CoyoteAdapter.postParseRequest()

CoyoteAdapter.postParseRequest()

上一方法中,经过connector获取service以后再取得对应的mapper,但是进来以后却没有看到对该mapper对象的构建,那该对象是哪里来的呢?

Mapper.map()

Mapper.map()

不知道你们还记不记得在第二篇中,在StandardService类中initInternal()startInternal()方法中有mapperListener方法的初始化和启动。

StandardService.initInternal()

StandardService.startInternal()

在该方法中查找到对应的host, context, wrapper

Mapper.internalMap()

Mapper.internalMap()

Mapper.internalMap()

回到CoyoteAdapter.postParseRequest(),经过Evaluste咱们能够看到当前请求对应的host, context, wrapper以及实例的映射均已找到。

CoyoteAdapter.postParseRequest()

接下来要作的就是根据链路组件进行一级级的调用,直至最后取出servlet执行。

CoyoteAdapter.service()

CoyoteAdapter.service()

先获得host,在经过host继续调用下一级组件

StandardEngineValve.invoke()

StandardEngineValve.invoke()

AbstractAccessLogValve.invoke()

AbstractAccessLogValve.invoke()

ErrorReportValve.invoke()

ErrorReportValve.invoke()

这里拿到context,继续invoke()

StandardHostValve.invoke()

StandardHostValve.invoke()

AuthenticatorBase.invoke()

AuthenticatorBase.invoke()

StandardContextValve.invoke()

StandardContextValve.invoke()

拿到wrapper以后,继续向下执行,从wrapper容器中获得servlet对象。

StandardWrapperValve.invoke()

StandardWrapperValve.invoke()

紧接着,把获得的servlet加入过滤器链中(可能有其它的处理,这里不直接进行处理),留待下面调用过滤器链再统一进行处理。

StandardWrapperValve.invoke()

StandardWrapperValve.invoke()

ApplicationFilterChain.doFilter()

ApplicationFilterChain.doFilter()

终于找到具体的实例了,太不容易了!!!

ApplicationFilterChain.internalDoFilter()

ApplicationFilterChain.internalDoFilter()

3、总结

Servlet请求链路

我收集有众多的 计算机电子书籍,有须要的小伙伴自提哦~
相关文章
相关标签/搜索