注册

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




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



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


一、请求分析


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


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


该组件接收到请求之后,会把相关请求进行封装,然后传递到engine组件中。


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


请求链路


二、深入探索


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


NioEndpoint.startInternal()


NioEndpoint.startInternal()


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


Poller.run()


Poller


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


Poller.run()


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


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()


三、总结


Servlet请求链路



我收集有众多的 计算机电子书籍,有需要的小伙伴自提哦~


0 个评论

要回复文章请先登录注册