乐趣区

关于tomcat:Tomcat源码学习第4篇-Servlet请求分析

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

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

一、申请剖析

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

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

该组件接管到申请之后,会把相干申请进行封装,而后传递到 engine 组件中。

紧跟着,engine组件会锁定对应的 hostcontext 以及wrapper,一层层的传递上来,找到最终解决申请的 servlet 实例。

二、深刻摸索

不晓得大家还有没有印象,在后面的文章中,咱们在 NioEndpoint 类中,启动 Accepter 线程的入口处上方还有着一个线程组在启动运行,然而却没有解说该线程是用来干嘛的~

NioEndpoint.startInternal()

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

Poller.run()

通过正文咱们能够晓得,该线程次要用于轮询已连贯的套接字,查看是否触发了事件,并在事件产生时将关联的套接字移交给对应的处理器。在源码中咱们能够看到 keyCount 变量记录着待处理申请数,提供给前面做相应判断。

持续往下走,通过 keyCount 判断是否有申请须要进行解决,需要的话则通过 selector.selectedKeys() 拿到须要被解决的 channel 汇合,进行循环解决。在 while 循环中咱们看到,所有就绪的通道都调用的是 processKey(sk, socketWrapper) 办法进行解决。

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

NioEndpoint.processKey()

进来之后咱们能够看到它首先在缓存池中尝试去获取一个解决线程,当缓存池中没有线程时,就创立一个新的线程,如果有的话就间接应用。

AbstractEndpoint.processSocket()

既然是线程了,那么咱们就关怀线程的外围办法即可。点击 SocketProcessorBase 跳转查看 run() 办法。

SocketProcessorBase.run()

doRun() 处打上断点,单击下一步,跳转到 NioEndpoint.doRun() 办法中。Poller线程移交到这边的线程进行解决,在该线程中须要失去以后的socket,做进一步的解决。

进入该办法之后,咱们能够看到它首先对 wrapper 进行判断,不为空再取出 socket,而后尝试着在connections 中去获取对应的 processor,如果获取不到,再尝试获取曾经解决过连贯,然而尚未销毁的processor 中去获取,还获取不到才进行创立。这样能够防止频繁的创立和销毁对象。

AbstractProtocol.process()

失去 processor 之后,调用 process 办法对报文进行解析。

进入该办法之后,咱们能够看到这外面是对 socketEvent 的状态进行判断,咱们以后申请次要是读状态,在此处打上断点,跳到该办法进来看一下。

AbstractProcessorLight.process()

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

Http11Processor.service()

在这里对原生的 requestresponse进行转换,失去 HttpServletRequestHttpServletResponse。而后依据申请信息找到可能解决以后申请的hostcontextwrapper

CoyoteAdapter.service()

在这办法能够看到它会通过 getMapper() 办法去匹配可能解决以后申请的 host,context,wrapper。到这里可能有的小伙伴会奇怪,为什么是从 mapper 中去匹配呢?这个问题留给你们去摸索一下,等下篇再给你们解答。

CoyoteAdapter.postParseRequest()

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

Mapper.map()

不晓得大家还记不记得在第二篇中,在 StandardService 类中 initInternal()startInternal()办法中有 mapperListener 办法的初始化和启动。

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

Mapper.internalMap()

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

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

CoyoteAdapter.service()

先失去 host,在通过host 持续调用下一级组件

StandardEngineValve.invoke()

AbstractAccessLogValve.invoke()

ErrorReportValve.invoke()

这里拿到context,持续invoke()

StandardHostValve.invoke()

AuthenticatorBase.invoke()

StandardContextValve.invoke()

拿到 wrapper 之后,持续向下执行,从 wrapper 容器中失去 servlet 对象。

StandardWrapperValve.invoke()

紧接着,把失去的 servlet 退出过滤器链中(可能有其它的解决,这里不间接进行解决),留待上面调用过滤器链再对立进行解决。

ApplicationFilterChain.doFilter()

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

ApplicationFilterChain.internalDoFilter()

三、总结

我收集有泛滥的 计算机电子书籍,有须要的小伙伴自提哦~

退出移动版