Tomcat源码架构笔记(servlet请求处理链路篇)

warning: 这篇文章距离上次修改已过1413天,其中的内容可能已经有所变动。

上一篇文章中,我们在debug模式下完整的跟踪程序走了一遍,对Tomcat start启动阶段所做的操作有了大概的了解。

这回,我们一起看看,Tomcat服务器接受到一个请求到处理这个请求之间经历了怎样的过程。


Servlet请求处理链路分析

  • 一个servlet如何被tomcat处理的?
  • servlet请求 -> 可以处理当前servlet请求的servlet实例 -> servlet.service()

Servlet请求处理链路和URL的对应关系Servlet请求处理链路和URL的对应关系

上一篇文章中我们知道Acceptor线程主要负责监听Socket嵌套字请求,并将其转给sekector(选择器)。

而poller线程,则负责检查sekector(选择器)中是否有数据到来的channel,如果有就进行处理。

NIO模型NIO模型

所以Servlet请求处理链路中最重要的就是poller线程,他负责控制这一系列的操作。


Servlet请求处理链路流程

Poller线程Poller线程


启动Poller线程和Acceptor线程

NioEndpoint.startInternal()NioEndpoint.startInternal()

我们可以看到在启动Acceptor线程,监听Socket之前,startInternal()方法先启动了Poller线程。

Poller类的核心方法就是run()方法。

他来负责循环检查selector中是否有事件就绪,并在数据可用的时候转给Worker线程。

NioEndpoint类 Poller.run() ①NioEndpoint类 Poller.run() ①

NioEndpoint类 Poller.run() ②NioEndpoint类 Poller.run() ②

处理Channel(可以理解为处理Socket)

NioEndpoint类 Poller.processKey()NioEndpoint类 Poller.processKey()


处理Socket请求具体方法

当Socket传进processSocket()方法中,先判断该Socket是否为空。

不为空,则开始在线程池中检查获取SocketProcessor处理线程(如果线程池中没有,就创建一个)。

AbstractEndpoint.processSocket() ①AbstractEndpoint.processSocket() ①

使用线程池技术执行SocketProcessor处理线程。

AbstractEndpoint.processSocket() ②AbstractEndpoint.processSocket() ②

其中核心处理方法就是该线程的run()方法。

SocketProcessorBase.run()SocketProcessorBase.run()

子类中doRun()方法的逻辑,首先取出对象,并判断握手状态。

NioEndpoint类 SocketProcessor.doRun()NioEndpoint类 SocketProcessor.doRun()

确定握手完成后,调用process()方法进一步处理。

NioEndpoint类 SocketProcessor.doRun()NioEndpoint类 SocketProcessor.doRun()

process方法(Handler处理请求)

AbstractProtocol.process()AbstractProtocol.process()

获取一个Processor(应用层协议)。

AbstractProtocol.process()AbstractProtocol.process()

调用该Processor(应用层协议)处理请求。

AbstractProtocol.process()AbstractProtocol.process()

HttpProcessor进行内容请求解析

AbstractProcessorLight.process()AbstractProcessorLight.process()

READ读状态处理(分支处理)

AbstractProcessorLight.process()AbstractProcessorLight.process()

应用层处理器HttpProcessor对请求进行解析处理

Http11Processor.service()Http11Processor.service()

解析封装原生Request/Response完成之后,要传入Adapter适配(由Adapter继续处理Request/Response

Http11Processor.service()Http11Processor.service()

CoyoteAdapter需要将Request/Response进一步封装为HttpServletRequest/HttpServletResponse

CoyoteAdapter.service()CoyoteAdapter.service()

根据URL找到可以处理该请求的对应组件(Mapper机制,详见下一节)

CoyoteAdapter.service()CoyoteAdapter.service()

CoyoteAdapter.postParseRequest()CoyoteAdapter.postParseRequest()

开始匹配

Mapper.map()Mapper.map()

真正的匹配逻辑

Mapper.internalMap()Mapper.internalMap()

request.getHost()就是从刚才封装好的mapperData中取出Host

StandardEngineValve.invoke()StandardEngineValve.invoke()

mapperDatamapperData

AbstractAccessLogValve.invoke()AbstractAccessLogValve.invoke()

ErrorReportValve.invoke()ErrorReportValve.invoke()

Host中要去进一步把处理当前请求的Context取出来

StandardHostValve.invoke()StandardHostValve.invoke()

把请求传递给context处理

StandardHostValve.invoke()StandardHostValve.invoke()

AuthenticatorBase.invoke()AuthenticatorBase.invoke()

寻找可以处理当前请求的wrapper

StandardContextValve.invoke()StandardContextValve.invoke()

调用wrapper容器处理请求

StandardContextValve.invoke()StandardContextValve.invoke()

寻找可以处理当前请求的Servlet

StandardWrapperValve.invoke()StandardWrapperValve.invoke()

把能够处理当前请求的servlet示例从wrapper容器中取出来

StandardWrapperValve.invoke()StandardWrapperValve.invoke()

为当前请求的处理生成一个过滤器链(在这个链路的核心逻辑要触发servlet调用)

StandardWrapperValve.invoke()StandardWrapperValve.invoke()

执行过滤器链

StandardWrapperValve.invoke()StandardWrapperValve.invoke()

过滤器链执行的核心方法

ApplicationFilterChain.doFilter()ApplicationFilterChain.doFilter()

请求被servlet实例处理了!!!

ApplicationFilterChain.internalDoFilter()ApplicationFilterChain.internalDoFilter()

控制台打印执行结果控制台打印执行结果


Mapper组件体系结构

Mapper组件体系结构Mapper组件体系结构

Tomcat中使用了Mapper机制重新封装了Host -> Context -> Wrapper(Servlet) 之间的数据和关系。

MapElementMapElement

在匹配出能够处理当前请求的Host -> Context -> Wrapper(Servlet) ,Mapper对象通过MapperListener.start();来完成初始化好了。

StandardService.startInternal()StandardService.startInternal()

StandardService -> StartInternal() -> MapperLintener.start()中完成Mapper对象初始化。

注册Host

MapperLintener.start()MapperLintener.start()

循环添加Host

MapperLintener.registerHost()MapperLintener.registerHost()

此时,Mapper对象已经初始化完毕了

HostHost

ContextContext

Wrapper(Servlet)Wrapper(Servlet)


OK,至此Tomcat基本的初始化、启动、处理请求的过程已经梳理一遍了。

Tomcat通过套娃的设计,带来了层级分明的结构;

Tomcat通过统一的生命周期管理接口,让各个组件有序工作;

Tomcat通过使用多线程带来了效率与性能的提升;

正确的debug方式,可以轻松深入代码解决问题;

评论已关闭