共计 2722 个字符,预计需要花费 7 分钟才能阅读完成。
前段时间家里有事忙,停更了好长一段时间,这里跟期待更新的小伙伴们说一声道歉,没能提前阐明一下,让小伙伴们等了这么久,真的不好意思!
后面说完了 Tomcat
的初始化和启动步骤,那么接下来就要进入重头戏了!在本篇文章中,我会跟后面一样,通过图文的形式来带着小伙伴们理解一个 Servlet
是如何被 tomcat
解决的,具体的解决链路都有哪些。
一、申请剖析
在《Tomcat 源码学习第 2 篇》中备注了各个组件的阐明。
当一个 servlet
申请到来的时候,首先通过的是 connector
组件,它是用来接管申请的。
该组件接管到申请之后,会把相干申请进行封装,而后传递到 engine
组件中。
紧跟着,engine
组件会锁定对应的 host
,context
以及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
类中,在该类外面对报文进行解析,封装原生的 request
和response
对象。这里的 response
因为咱们还没有到返回的步骤,所以只是做个初步的参数设置。后续要传入 Adapter
进行下一步操作。
Http11Processor.service()
在这里对原生的 request
和response
进行转换,失去 HttpServletRequest
和HttpServletResponse
。而后依据申请信息找到可能解决以后申请的host
,context
,wrapper
。
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()
三、总结
我收集有泛滥的 计算机电子书籍,有须要的小伙伴自提哦~