# Tomcat容器内部原理

Servlet(server applet 服务端小程序)是一种国际组织的协议、约定.\
Tomcat只是针对Servlet协议的规范做了封装，其他这样的软件还有jetty等.

## Tomcat总体结构 <a href="#tomcat-zong-ti-jie-gou" id="tomcat-zong-ti-jie-gou"></a>

Server –> Service –>\
Connector & Container( Engine –> Host –> Context( Wrapper( Servlet ) ) )

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hL1v9x-TlQlqcv%2Fimport-tomcat01.png?generation=1573735150207057\&alt=media)

Tomcat 的心脏是两个组件：Connector 和 Container，Connector 组件是可以被替换，这样可以提供给服务器设计者更多的选择，因为这个组件是如此重要，不仅跟服务器的设计的本身，而且和不同的应用场景也十分相关，所以一个 Container 可以选择对应多个 Connector。

多个 Connector 和一个 Container 就形成了一个 Service，有了 Service 就可以对外提供服务了，但是 Service 还要一个生存的环境，必须要有人能够给她生命、掌握其生死大权，那就非 Server 莫属了。所以整个 Tomcat 的生命周期由 Server 控制。

### Server <a href="#server" id="server"></a>

前面说一对情侣因为 Service 而成为一对夫妻，有了能够组成一个家庭的基本条件，但是它们还要有个实体的家，这是它们在社会上生存之本，有了家它们就可以安心的为人民服务了，一起为社会创造财富。

Server 要完成的任务很简单，就是要能够提供一个接口让其它程序能够访问到这个 Service 集合、同时要维护它所包含的所有 Service 的生命周期，包括如何初始化、如何结束服务、如何找到别人要访问的 Service。还有其它的一些次要的任务，如您住在这个地方要向当地政府去登记啊、可能还有要配合当地公安机关日常的安全检查什么的。

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hNzogmsooLhXQ5%2Fimport-tomcat02.png?generation=1573735150546323\&alt=media)

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hPrRbiZc7i09hU%2Fimport-tomcat03.png?generation=1573735150141342\&alt=media)

### Service <a href="#service" id="service"></a>

我们将 Tomcat 中 Connector、Container 作为一个整体比作一对情侣的话，Connector 主要负责对外交流，可以比作为 Boy，Container 主要处理 Connector 接受的请求，主要是处理内部事务，可以比作为 Girl。那么这个 Service 就是连接这对男女的结婚证了。是Service 将它们连接在一起，共同组成一个家庭。当然要组成一个家庭还要很多其它的元素

说白了，Service 只是在 Connector 和 Container 外面多包一层，把它们组装在一起，向外面提供服务，一个 Service 可以设置多个 Connector，但是只能有一个 Container 容器。这个 Service 接口的方法列表如下:

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hRToF3d9tDl5jH%2Fimport-tomcat04.png?generation=1573735150160380\&alt=media)

### Container <a href="#container" id="container"></a>

Container是一个接口，定义了下属的各种容器，尤其是Wrapper、Host、Engine、Context

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hTvA88_dBlYQ8o%2Fimport-tomcat05.png?generation=1573735150079635\&alt=media)

### Engine <a href="#engine" id="engine"></a>

负责处理来自相关联的service的所有请求，处理后，将结果返回给service，而connector是作为service与engine的中间媒介出现的。\
一个engine下可以配置一个默认主机，每个虚拟主机都有一个域名。当engine获得一个请求时，它把该请求匹配到虚拟主机(host)上，然后把请求交给该主机来处理。\
Engine有一个默认主机，当请求无法匹配到任何一个虚拟主机时，将交给默认host来处理。Engine以线程的方式启动Host。

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hVDGlHrjp0F0vO%2Fimport-tomcat06.png?generation=1573735150145622\&alt=media)

### Host <a href="#host" id="host"></a>

代表一个虚拟主机，每个虚拟主机和某个网络域名（Domain Name）相匹配。\
每个虚拟主机下都可以部署一个或多个web应用，每个web应用对应于一个context，有一个context path。\
当Host获得一个请求时，将把该请求匹配到某个Context上，然后把该请求交给该Context来处理匹配的方法是“最长匹配”，所以一个path==””的Context将成为该Host的默认Context所有无法和其它Context的路径名匹配的请求都将最终和该默认Context匹配。

### Context <a href="#context" id="context"></a>

一个Context对应于一个Web应用，一个Web应用由一个或者多个Servlet组成，Context在创建的时候将根据配置文件$CATALINA\_HOME/conf/web.xml和$WEBAPP\_HOME/WEB-INF/web.xml载入Servlet类。当Context获得请求时，将在自己的映射表(mapping table)中寻找相匹配的Servlet类，如果找到，则执行该类，获得请求的回应，并返回。

### Wrapper <a href="#wrapper" id="wrapper"></a>

Wrapper 代表一个 Servlet，它负责管理一个 Servlet，包括的 Servlet 的装载、初始化、执行以及资源回收。Wrapper 是最底层的容器，它没有子容器了，所以调用它的 addChild 将会报错。\
Wrapper 的实现类是 StandardWrapper，StandardWrapper 还实现了拥有一个 Servlet 初始化信息的 ServletConfig，由此看出 StandardWrapper 将直接和 Servlet 的各种信息打交道。

### Connector <a href="#connector" id="connector"></a>

Connector将在某个指定的端口上来监听客户的请求，把从socket传递过来的数据，封装成Request，传递给Engine来处理，并从Engine处获得响应并返回给客户。

Tomcat通常会用到两种Connector：

1. Http Connector 在端口8080处侦听来自客户browser的http请求。 AJP Connector
2. 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。

### Lifecycle <a href="#lifecycle" id="lifecycle"></a>

现实生活中大部分的事物都有生命周期，就像人的生老病死一样。

在编程中也有很多对象是具有生命周期的，从初始化、运行、回收等 会经历几个不同的阶段。 在tomcat中容器相关的好多组建都实现了Lifecycle接口，当tomcat启动时，其依赖的下层组件会全部进行初始化。 并且可&#x4EE5;***对每个组件生命周期中的事件添加监听器***。

例如当服务器启动的时候，tomcat需要去调用servlet的init方法和初始化容器等一系列操作，而停止的时候，也需要调用servlet的destory方法。而这些都是通过org.apache.catalina.Lifecycle接口来实现的。由这个类来制定各个组件生命周期的规范。

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hXZ9t6ZeddP-bY%2Fimport-tomcat07.png?generation=1573735150182491\&alt=media)

下图是生命周期的所有状态

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hZvmT5JyD8a_ht%2Fimport-tomcat08.png?generation=1573735150170033\&alt=media)

### LifecycleListener <a href="#lifecyclelistener" id="lifecyclelistener"></a>

在Lifecycle的介绍中提到，Lifecycle会对每个组件生命周期中的事件添加监听器，也就是addLifecycleListener(LifecycleListener listener)方法，而LifecycleListener就是上面提到的监听器。

### LifecycleEvent <a href="#lifecycleevent" id="lifecycleevent"></a>

顾名思义，就是当有监听事件发生的时候，LifecycleEvent会存储时间类型和数据

```
/**
* Construct a new LifecycleEvent with the specified      parameters.
*
* @param lifecycle Component on which this event occurred
* @param type Event type (required)
* @param data Event data (if any)
*/
public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
   super(lifecycle);
   this.type = type;
   this.data = data;
}
```

## Tomcat启动过程 <a href="#tomcat-qi-dong-guo-cheng" id="tomcat-qi-dong-guo-cheng"></a>

以嵌入式的tomcat-embed-core:8.5.6为例，以编程的方式启动tomcat，可以看到调用了server.start()

Tomcat的start()方法

```
/**
 * Start the server.
 *
 * @throws LifecycleException Start error
 */
public void start() throws LifecycleException {
    getServer();
    getConnector();
    server.start();
}
```

server的默认实现是StandardServer, start()方法在其父类LifecycleBase中

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9haz56b4nBuM14L%2Fimport-tomcat09.png?generation=1573735150232096\&alt=media)

***声明：***&#x9664;了StandardServer, 这个类也是StandardService, StandardEngine, StandardHost, StandardContext的父类，所以当调用这些类的start()方法时其实都是调用此处的start()方法，而最重要的是在start()方法中会调用startInternal()，startInternal()在LifecycleBase中是抽象方法，具体实现由各实现类去做，在此后不再赘述。

### 启动server <a href="#qi-dong-server" id="qi-dong-server"></a>

启动server其实就是启动service容器

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hcq2FGZyvyko3F%2Fimport-tomcat10.png?generation=1573735150121085\&alt=media)

### 启动service <a href="#qi-dong-service" id="qi-dong-service"></a>

启动service其实就是启动engine容器和connector容器

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9he0xZ9kNZY9Gpe%2Fimport-tomcat11.png?generation=1573735150861138\&alt=media)

### 启动Engine <a href="#qi-dong-engine" id="qi-dong-engine"></a>

启动engine其实就是启动host容器(多线程)

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hgTQP8cdmVYQGZ%2Fimport-tomcat12.png?generation=1573735150077057\&alt=media)

### 启动Host <a href="#qi-dong-host" id="qi-dong-host"></a>

启动Host的方式和上图一样，都是以线程的方式启动子Container，这里Host的children为Context

### 启动Context <a href="#qi-dong-context" id="qi-dong-context"></a>

启动Wrapper![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hi7PF7w4UfLxu-%2Fimport-tomcat13.png?generation=1573735150336780\&alt=media)

下面看一下非常重要的一个方法 loadServlet:

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hki4fmec_2fILK%2Fimport-tomcat14.png?generation=1573735150527851\&alt=media)

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hmB4je50a_Kafw%2Fimport-tomcat15.png?generation=1573735150212855\&alt=media)

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hozkPvDIxpsoKq%2Fimport-tomcat16.png?generation=1573735150164082\&alt=media)

它基本上描述了对 Servlet 的操作，当装载了 Servlet 后就会调用 Servlet 的 init 方法，同时会传一个 StandardWrapperFacade 对象给 Servlet，这个对象包装了 StandardWrapper，ServletConfig 与它们的关系图如下：\
![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hqPgdkNNw0Jw_a%2Fimport-tomcat17.png?generation=1573735150098866\&alt=media)

### 启动Connector <a href="#qi-dong-connector" id="qi-dong-connector"></a>

Tomcat的Connector是Coyote connector的一种实现，这是tomcat的官方解释：The Coyote HTTP/1.1 Connector element represents a Connector component that supports the HTTP/1.1 protocol. It enables Catalina to function as a stand-alone web server, in addition to its ability to execute servlets and JSP pages.\
Tomcat8之后默认使用nio作为接受请求策略，默认在Service启动的时候进行初始化，当然也可以单独启动，在默认的构造函数中会初始化ProtocolHandler

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hs-BRavGLdPxvS%2Fimport-tomcat18.png?generation=1573735150109237\&alt=media)

tomcat中支持两种协议的连接器：HTTP/1.1与AJP/1.3

HTTP/1.1协议负责建立HTTP连接，web应用通过浏览器访问tomcat服务器用的就是这个连接器，默认监听的是8080端口；

AJP/1.3协议负责和其他HTTP服务器建立连接，监听的是8009端口，比如tomcat和apache或者iis集成时需要用到这个连接器。

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9huj3DltWfZ0nuj%2Fimport-tomcat19.png?generation=1573735150157575\&alt=media)

Connector的启动其实就是ProtocolHandler的启动，如下图：

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hw-yx4y3p2QpLH%2Fimport-tomcat20.png?generation=1573735150230428\&alt=media)

ProtocolHandler的类结构如下图：

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9hysJtKcOixZZKb%2Fimport-tomcat21.png?generation=1573735150235238\&alt=media)

Connector的startInternal方法调用了ProtocolHandle的start方法，这个start方法就在AbstractProtocol中，如下图：

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9i-UqwiffSuLbJ7%2Fimport-tomcat22.png?generation=1573735150502146\&alt=media)

经历了这么多，终于快到终点了，这时候又出来了一个EndPoint，不过不要急，这个EndPoint已经算是终点了，EndPoint是咱们Tomcat启动的Socket管理者(注意：通过类图可以看出AbstractEndpoint已经脱离了Lifecycle和LifecycleListener体系，所以它只是一个简简单单的Socket管理者)，因为是由他直接启动默认的Nio，在启动的时候先看看类结构图：

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9i1oVLp9EkSsDpl%2Fimport-tomcat23.png?generation=1573735150129957\&alt=media)

再看一下EndPoint能做什么，看方法就知道了

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9i3cnhK1X00jZGj%2Fimport-tomcat24.png?generation=1573735150282641\&alt=media)

createAcceptor、createExecutor等方法都是在初始化EndPoint很重要方法，因为在接收请求的时候，通过Acceptor的接收，经过重重模块，才能一路到达最后的Servlet，这个在后面会有讲到。

那么EndPoint最后的启动，看下图：

![](https://530416962-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LtduzqE1l4TlPC2D3SH%2F-Ltdv-y6sBZiYDUuy_7L%2F-Ltdv9i50KfGR-I27FDz%2Fimport-tomcat25.png?generation=1573735150633334\&alt=media)

在这个地方会启动很多的线程，第一次读这段代码的同学可能会有点乱，因为有想过启动线程，现在还有些不知道是为什么启动，这就是tomcat的有意思之处了，因为下一篇我会说到tomcat接收请求的流程，所以在下一篇文章我会详细讲解。

好了，大家，tomcat 的启动流程大概就是这样，当然这里面还有一些更细节的知识，此文章适合第一次想要了解Tomcat的同学，当然以后我也会一直完善这篇文章，有看到有意思的部分我也会补充的。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tuonioooo-notebook.gitbook.io/performance-optimization/webrong-qi-you-hua/tomcatrong-qi-you-hua-pian/tomcatrong-qi-nei-bu-yuan-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
