> For the complete documentation index, see [llms.txt](https://tuonioooo-notebook.gitbook.io/performance-optimization/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tuonioooo-notebook.gitbook.io/performance-optimization/webrong-qi-you-hua/tomcatrong-qi-you-hua-pian/tomcatrong-qi-nei-bu-yuan-li.md).

# 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 ) ) )

![](/files/-Ltdv9hL1v9x-TlQlqcv)

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。还有其它的一些次要的任务，如您住在这个地方要向当地政府去登记啊、可能还有要配合当地公安机关日常的安全检查什么的。

![](/files/-Ltdv9hNzogmsooLhXQ5)

![](/files/-Ltdv9hPrRbiZc7i09hU)

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

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

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

![](/files/-Ltdv9hRToF3d9tDl5jH)

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

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

![](/files/-Ltdv9hTvA88_dBlYQ8o)

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

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

![](/files/-Ltdv9hVDGlHrjp0F0vO)

### 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接口来实现的。由这个类来制定各个组件生命周期的规范。

![](/files/-Ltdv9hXZ9t6ZeddP-bY)

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

![](/files/-Ltdv9hZvmT5JyD8a_ht)

### 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中

![](/files/-Ltdv9haz56b4nBuM14L)

***声明：***&#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容器

![](/files/-Ltdv9hcq2FGZyvyko3F)

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

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

![](/files/-Ltdv9he0xZ9kNZY9Gpe)

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

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

![](/files/-Ltdv9hgTQP8cdmVYQGZ)

### 启动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![](/files/-Ltdv9hi7PF7w4UfLxu-)

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

![](/files/-Ltdv9hki4fmec_2fILK)

![](/files/-Ltdv9hmB4je50a_Kafw)

![](/files/-Ltdv9hozkPvDIxpsoKq)

它基本上描述了对 Servlet 的操作，当装载了 Servlet 后就会调用 Servlet 的 init 方法，同时会传一个 StandardWrapperFacade 对象给 Servlet，这个对象包装了 StandardWrapper，ServletConfig 与它们的关系图如下：\
![](/files/-Ltdv9hqPgdkNNw0Jw_a)

### 启动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

![](/files/-Ltdv9hs-BRavGLdPxvS)

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

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

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

![](/files/-Ltdv9huj3DltWfZ0nuj)

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

![](/files/-Ltdv9hw-yx4y3p2QpLH)

ProtocolHandler的类结构如下图：

![](/files/-Ltdv9hysJtKcOixZZKb)

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

![](/files/-Ltdv9i-UqwiffSuLbJ7)

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

![](/files/-Ltdv9i1oVLp9EkSsDpl)

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

![](/files/-Ltdv9i3cnhK1X00jZGj)

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

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

![](/files/-Ltdv9i50KfGR-I27FDz)

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

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