在上一篇文章中,我们在debug模式下完整的跟踪程序走了一遍,对Tomcat start启动阶段所做的操作有了大概的了解。
这回,我们一起看看,Tomcat服务器接受到一个请求到处理这个请求之间经历了怎样的过程。

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

在上一篇文章中我们知道Acceptor线程主要负责监听Socket嵌套字请求,并将其转给sekector(选择器)。
而poller线程,则负责检查sekector(选择器)中是否有数据到来的channel,如果有就进行处理。

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

启动Poller线程和Acceptor线程

我们可以看到在启动Acceptor线程,监听Socket之前,startInternal()方法先启动了Poller线程。
Poller类的核心方法就是run()方法。
他来负责循环检查selector中是否有事件就绪,并在数据可用的时候转给Worker线程。


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

处理Socket请求具体方法
当Socket传进processSocket()方法中,先判断该Socket是否为空。
不为空,则开始在线程池中检查获取SocketProcessor处理线程(如果线程池中没有,就创建一个)。

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

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

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

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

process方法(Handler处理请求)

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

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

HttpProcessor进行内容请求解析

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

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

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

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

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


开始匹配

真正的匹配逻辑

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




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

把请求传递给context处理


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

调用wrapper容器处理请求

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

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

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

执行过滤器链

过滤器链执行的核心方法



Mapper组件体系结构

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

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

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

循环添加Host

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



OK,至此Tomcat基本的初始化、启动、处理请求的过程已经梳理一遍了。
Tomcat通过套娃的设计,带来了层级分明的结构;
Tomcat通过统一的生命周期管理接口,让各个组件有序工作;
Tomcat通过使用多线程带来了效率与性能的提升;
正确的debug方式,可以轻松深入代码解决问题;