tomcat是组件化的模型,分为不同的层次。
连接器、容器是真正干活儿的组件。高层就是管理这些底层干活儿组件的组件。

startup.sh 脚本,启动一个 JVM 来运行 Tomcat 的启动类 Bootstrap。

Bootstrap 启动类,会初始化 Tomcat 的类加载器,创建 Catalina。

Catalina 启动类,解析server.xml,创建 Server。

Server 管理 Service,Service 管理连接器和容器。

image.png

Catalina:

启动:解析 server.xml,创建配置的各种组件,调用 Server 的 init()、start(),Tomcat 就启动起来了。
关闭:通过“Ctrl + C”可以关闭 Tomcat。Catalina 在 JVM 中注册一个“关闭钩子”。
kill -9 强杀的话,JVM钩子也没办法执行到。
在 Server 关闭时做一些清理工作,比如将缓存数据刷到磁盘上,或者清理一些临时文件,可以向 JVM 注册一个“关闭钩子”,其实就是一个线程,JVM 在停止之前会尝试执行这个线程的 run 方法。

Server

Server 组件的具体实现类是 StandardServer。
主要是管理 Service 组件。

Server 如何管理若干 Service 组件?
Server在添加的过程中动态地扩展数组长度,当添加一个新的 Service 实例时,会创建一个新数组并把原来数组内容复制到新数组,这样是为了节省内存空间。

启动一个 Socket 来监听停止端口!如果有新的连接到来就建立连接。
从 Socket 中读取数据,看是否是停止命令“SHUTDOWN”,

Service

Service 先启动了 Engine 组件,再启动 Mapper 监听器,最后才是启动连接器,这是基于它们的依赖关系。
内层组件启动好了才能对外提供服务,才启动外层的连接器组件。Mapper 也依赖容器组件,容器组件启动好了才能监听它们的变化,因此 Mapper 和 MapperListener 在容器组件之后启动。
组件停止的顺序跟启动顺序正好相反的。

Engine

顶层的容器组件 Engine 具体是如何实现的?
Engine 本质是一个容器,继承了 ContainerBase 基类,并且实现了 Engine 接口。
Engine 的子容器是 Host,它持有了一个 Host 容器的数组。具体实现是,ContainerBase 用 HashMap 保存了它的子容器。ContainerBase 会用专门的线程池来启动子容器。

Engine 不仅负责子容器的生命周期,还把请求转发到 Host 容器。

请求的具体实现?

Engine 容器通过 Valve 来实现把请求转发给某一个 Host 子容器来处理。
Host 容器对象是从请求中拿到的,请求对象中怎么会有 Host 容器呢?
在请求到达 Engine 容器中之前,Mapper 组件已经对请求进行了路由处理,Mapper 组件通过请求的 URL 定位了相应的容器,并且把容器对象保存到了请求对象中。

设计思想

设计就是要找到系统的变化点和不变点。对于组件的生命周期管理这一功能模块,分析变化点和不变点:

不变点是每个组件都要经历的状态,以及状态变化的过程:创建、初始化、启动、结束。
变化点是具体组件的启动方法。

把不变点抽象出来成为一个接口,这个接口跟生命周期有关,叫作 Lifecycle。Lifecycle 接口里应该定义这么几个方法:init、start、stop 和 destroy,每个具体的组件去实现这些方法。

调用者可以无差别的调用各组件的 init 方法和 start 方法,这就是组合模式的妙处。
只要调用最顶层 Server 组件的 init 和 start 方法,整个 Tomcat 启动起来了。

Server 组件的在启动连接器和容器时,都分别加了锁,这是为什么呢?