整体架构
Tomcat 启动时沿组件树向下传播,运行时沿 Connector、协议处理器和容器 Pipeline 流动。
先给答案:启动走组件树,请求走协议层与容器树
Section titled “先给答案:启动走组件树,请求走协议层与容器树”Tomcat 启动时由 Catalina 构建 Server -> Service -> Connector + Engine 组件树,再通过 Lifecycle 向下初始化和启动;请求到来后,Connector/Coyote 负责 socket 与 HTTP,Mapper 选择 Host、Context、Wrapper,Valve 链最终调用 Servlet。
两条主线的边界必须分开:线程池和协议解析属于连接器,应用路由与 Servlet 生命周期属于容器,Web 应用类加载器还要在停止时清理线程、ThreadLocal 和驱动。混淆这些所有权会造成调优方向错误或重部署泄漏。
Bootstrap/Catalina -> Server -> Service -> Connector + Engine Engine -> Host -> Context -> Wrapper -> Servletsocket -> NioEndpoint -> Http11Processor -> CoyoteAdapter -> Mapper -> EngineValve -> HostValve -> ContextValve -> WrapperValve -> ServletCatalina#start 位于 java/org/apache/catalina/startup/Catalina.java:868;LifecycleBase#init 与 start 位于 java/org/apache/catalina/util/LifecycleBase.java:121、:145。Service 启动在 StandardService.java:424,Connector 初始化/启动在 Connector.java:1255、:1300。
NioEndpoint#processKey 位于 java/org/apache/tomcat/util/net/NioEndpoint.java:1129,Http11Processor#service 位于 java/org/apache/coyote/http11/Http11Processor.java:254,CoyoteAdapter#service 位于 java/org/apache/catalina/connector/CoyoteAdapter.java:305,Mapper#map 位于 java/org/apache/catalina/mapper/Mapper.java:688。之后由 StandardEngineValve.java:54 开始逐级进入容器。
替代方案:把协议解析、路由和 Servlet 调用放进一个请求处理器。 为什么不行:协议演进、虚拟主机、Web 应用生命周期和类加载隔离会互相污染。Tomcat 用 Coyote/Catalina Adapter、Mapper 和 Valve 分开承担边界。
| 风险 | 触发条件 | 诊断入口 |
|---|---|---|
| 启动失败 | 生命周期子组件异常 | LifecycleBase 状态与根因日志 |
| 请求排队 | Endpoint 或 Servlet 阻塞 | 线程池、队列、慢请求 |
| 路由错误 | Host/Context/Wrapper 不匹配 | Mapper 与部署命名 |
| 热部署泄漏 | Webapp 类仍被外部引用 | WebappClassLoaderBase 停止清理 |
面试锚点
- 画出 Tomcat 从 socket 到 Servlet 的调用链。
- Connector 与 Container 为什么分离?
- Mapper 和 Valve 的职责是什么?