Tomcat源码架构笔记(servlet请求处理链路篇)
在上一篇文章中,我们在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方式,可以轻松深入代码解决问题;