Fellow Travellers

Tomcat流程浅析

万世威
字数统计: 8.1k阅读时长: 42 min
2018/12/04 Share

Tomcat

整体架构图

​ Tomcat 中最顶层的容器叫 Server(代表整个服务器),Server中包含至少一个Service(用于提供服务)。

​ Service主要包含两个部分:Connector和Container。他们的作用如下:

1、Connector用于处理连接相关的事情,并提供Socket与Request和Response相关的转化;

2、Container用于封装和管理Servlet,以及具体处理Request请求;

一个Tomcat只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但是可以有多个Connectors,这是因为一个服务可以有多个连接,如同时提供Http和Https链接,也可以提供向相同协议不同端口的连接。

在Tomcat中conf下Server.xml 中:

Server标签设置的端口号为8005,shutdown=”SHUTDOWN” ,表示在8005端口监听“SHUTDOWN”命令,如果接收到了就会关闭Tomcat。一个Server有一个Service,当然还可以进行配置,一个Service有多个,Service左边的内容都属于Container的,Service下边是Connector。

Tomcat里的Server由 org.apache.catalina.startup.Catalina管理。Catalina类里面由 loadstartstop三个方法用来管理整个服务器对的生命周期。

1、load:用于根据 conf/server.xml 文件创建Server、并调用Server的init方法进行初始化。

2、start:用于启动服务器。

3、stop:停止服务器。

start、stop方法在内部会调用Server的start和stop方法,load方法调用Server的init方法。

Server的start方法会调用Service中的start方法,Service中的start会调用Connectors和Container中的start方法。

Tomcat启动类

Main方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// org.apache.catalina.startup.Bootstrap
public static void main(String args[]) {
if (daemon == null) {
// 先创建一个 bootstrap 实例
Bootstrap bootstrap = new Bootstrap();
try {
// 初始化ClassLoader:
// ClassLoader commonLoader = null;
// ClassLoader catalinaLoader = null; 绑定到当前线程上
// ClassLoader sharedLoader = null;
// catalinaLoader类加载器创建Catalina实例,赋值给catalinaDaemon
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}

try {
// 默认执行 start 方法
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}

if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
// 执行这个位置,【让主线程不退出】
// if the daemon should block: true
daemon.setAwait(true);
// 反射调用 catalinaDaemon#load 方法,根据server.xml 创建服务
daemon.load(args);
// 反射调用 catalinaDaemon#start 方法,启动服务
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null==daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
}
}

Catalina类

负责整个生命周期

1
2
org.apache.catalina.startup.Catalina
// 主要看 setAwait、load、start、stop方法

Await方法

1
2
3
4
// 设置 await值,会在start方法中服务器启动完成后来判断是否进入等待状态
public void setAwait(boolean b) {
await = b;
}

Load方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// org.apache.catalina.startup.Catalina
public void load() {
// 已经加载就返回
if (loaded) {
return;
}
// 设置加载状态
loaded = true;
long t1 = System.nanoTime();

initDirs();
// Before digester - it may be needed
initNaming();
// Create and execute our Digester
// 通过 digester 解析 server.xml 配置文件
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
try {
// 指定了 conf/server.xml
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
}
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream(getConfigFile());
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(getConfigFile()).toString());
}
}

// This should be included in catalina.jar
// Alternative: don't bother with xml, just create it manually.
if (inputStream == null) {
// 尝试加载 server-embed.xml
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream("server-embed.xml");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource("server-embed.xml").toString());
}
}

// 依旧找不到文件,返回
if (inputStream == null || inputSource == null) {
return;
}

try {
inputSource.setByteStream(inputStream);
digester.push(this);
// 解析 xml
digester.parse(inputSource);
}
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
}

getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

// Stream redirection
initStreams();

// Start the new server
try {
getServer().init();
}
}

简化后的 load方法:

1
2
3
4
5
6
7
8
9
Digester digester = createStartDigester();
File file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
getServer().setCatalina(this);
getServer().init();

​ Digester 查相关资料:Java解析xml主要由DOM4J(一次读取到内存并解析)、SAX(一次解析一部分),digester本身采用SAX的解析方式,并提供了一层包装,对使用者更加友好,后来独立出来成为apache的Commons下面的一个单独的子项目

Init方法

Server接口只有一个StandardSever实现类

public final class StandardServer extends LifecycleMBeanBase implements Server {}
public abstract class LifecycleMBeanBase extends LifecycleBase implements JmxEnabled {}

// 调用父类org.apache.catalina.util.LifecyleBase的init方法进行初始化操作
@Override
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
    try {
        // 设置生命周期 INITIALIZING,(枚举类)
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        // 具体初始化方法
        initInternal();
        // 设置生命周期 INITIALIZED,(枚举类)
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    }
}

Start方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// org.apache.catalina.startup.Catalina
public void start() {

if (getServer() == null) {
load();
}

if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}

long t1 = System.nanoTime();

// Start the new server
try {
// 调用Server的start方法启动服务器
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}

long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}

// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);

// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}

if (await) {
await();
stop();
}
}

getServer().start()分析:LifecycleBase#start()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Override
public final synchronized void start() throws LifecycleException {

// 判断 生命周期的状态,在 init 方法中设置的枚举状态
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {

if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}

return;
}
// 如果没有初始化先进行初始化,如果启动失败则关闭,如果状态无法处理则抛出异常
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}

try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
// 此处 循环调用 start 方法,server->service->connector..
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
}
}

流程图和 Init 方法类似。

Stop方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// org.apache.catalina.startup.Catalina
public void stop() {
try {
// Remove the ShutdownHook first so that server.stop()
// doesn't get invoked twice
if (useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);

// If JULI is being used, re-enable JULI's shutdown to ensure
// log messages are not lost
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
true);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}

// Shut down the server
try {
Server s = getServer();
LifecycleState state = s.getState();
if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0
&& LifecycleState.DESTROYED.compareTo(state) >= 0) {
// Nothing to do. stop() was already called
} else {
s.stop();
s.destroy();
}
} catch (LifecycleException e) {
log.error("Catalina.stop", e);
}
}

启动流程

Tomcat的生命周期

Lifecycle接口

​ Tomcat通过 org.apache.catalina.Lifecycle接口统一管理生命周期。一共做了4件事:

1、定义13个string类型常量,用于LifecycleEvent时间的type属性中,用于区分组件发出的LifecycleEvent事件时的状态。

2、定义三个管理监听器的方法,addLifecycleListener、findLifecycleListeners、removeLifecycleListener。

3、定义4个生命周期的方法,init、start、stop、destory,用于执行生命周期的各个阶段的操作。

4、定义了获取当前状态的两个方法,getState、getStateName、用于获取当前的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface Lifecycle {
// 13个状态常量值
public static final String BEFORE_INIT_EVENT = "before_init";
public static final String AFTER_INIT_EVENT = "after_init";
public static final String START_EVENT = "start";
public static final String BEFORE_START_EVENT = "before_start";
public static final String AFTER_START_EVENT = "after_start";
public static final String STOP_EVENT = "stop";
public static final String BEFORE_STOP_EVENT = "before_stop";
public static final String AFTER_STOP_EVENT = "after_stop";
public static final String AFTER_DESTROY_EVENT = "after_destroy";
public static final String BEFORE_DESTROY_EVENT = "before_destroy";
public static final String PERIODIC_EVENT = "periodic";
public static final String CONFIGURE_START_EVENT = "configure_start";
public static final String CONFIGURE_STOP_EVENT = "configure_stop";
// 3个监听器方法
public void addLifecycleListener(LifecycleListener listener);
public LifecycleListener[] findLifecycleListeners();
public void removeLifecycleListener(LifecycleListener listener);
// 4个生命周期方法
public void init() throws LifecycleException;
public void start() throws LifecycleException;
public void stop() throws LifecycleException;
public void destroy() throws LifecycleException;
// 2个当前状态方法
public LifecycleState getState();
public String getStateName();

LifecyleBase

​ Lifecycle的默认实现是org.apche.catalina.util.LifecycleBase,所有实现了生命周期的组件都直接或者间接的继承自LifecycleBase。

​ 监听管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//org.apache.catalina.util.LifecycleBase
private final List<LifecycleListener> lifecycleListeners =
new CopyOnWriteArrayList<>();

@Override
public void addLifecycleListener(LifecycleListener listener) {
lifecycleListeners.add(listener);
}

@Override
public LifecycleListener[] findLifecycleListeners() {
return lifecycleListeners.toArray(new LifecycleListener[0]);
}

@Override
public void removeLifecycleListener(LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
// 由 standardContext#startInternal来注入监听器

​ 4个周期方法,看上面的分析。

​ 2个获取状态的方法:

1
2
3
4
5
6
7
8
9
@Override
public LifecycleState getState() {
return state;
}

@Override
public String getStateName() {
return getState().toString();
}

Container

​ Container是Tomcat中容器的接口,使用的Servlet就封装在其子接口Wrapper中,Container一共有有4个子接口,Engine、Host、Context、Wrapper和一个默认实现类ContainerBase,每个子接口都是一个容器,这4个子容器都有一个对应的 StandardXXX实现类,这些实现类都继承自 LifecycleBase。看如下结构图:

4个容器的作用:

Container的4个容器是逐层包含的关系。它们之间的关系如下图:

1、Engine:用来管理多个站点,一个Service最多只能有一个Engine。

2、Host:代表一个站点,通过配置Host可以添加站点。

3、Context:代表一个应用程序,对应一个WEB-INF目录。

4、Wrapper:每个Wrapper封装一个Servlet。

4个容器的配置方法

1、Engine和Host的配置 都在 conf/server.xml 中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
</Host>
</Engine>
</Service>
</Server>
  • Server 在 8005端口监听关闭命令“SHUTDOWN”;
  • Server 中定义了一个 Catalina的Service;
  • Service 中定义了两个Connector:
    • 一个是HTTP协议;
    • 一个是AJP协议(用于集成);
  • Service 中还定义了了一个Catalina的Engine;
  • Engine 中定义了 localhost 的 Host;
    • defaultHost:请求的域名如果在所有的Host的name和Alias中都找不到使用的默认值
  • Host:
    • name:表示域名;
    • appBase:站点的位置;
    • unpackWARS:是否自动解压war包;
    • autoDeploy:是否自动部署;
    • 子标签: excelib.com:给localhost定义别名;

2、Context通过文件配置的方式一共有5个位置可以配置:

  • conf/server.xml中的Context标签;
  • conf/[enginename]/[hostname]/目录下以应用命名的 xml 文件。
  • 应用自己的 /META-INT/context.xml;
  • conf/context.xml 文件
  • conf/[enginename]/[hostname]/context.xml.default文件;

前三个用于配置单独的应用,后面2种是Context共享的。第4种是 整个 Tomcat 共享,第5种配置的内容在对应的站点(Host)中共享。第1种方式只有在Tomcat重启才会重新加载,不推荐使用。

<!-- 
    用于全局配置
    The contents of this file will be loaded for each web application 
-->
<Context>

    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
</Context>

3、Wrapper的配置,在web.xml中配置的Servlet,一个Servlet对应一个Wrapper、可以在 conf/web.xml 中配置全局的 Wrapper,处理 Jsp的 JspServlet的配置等。

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

<!--还配置了 session 超时时间-->
<session-config>
    <session-timeout>30</session-timeout>
</session-config>

<!-- 很多 mime 类型 -->
<mime-mapping>
</mime-mapping>

Container的启动

​ Container也是通过 init、start方法来完成的。也是通过 initInternal和 startInternal 方法具体处理。启动方式有点如下区别:

  • Container的4个子容器有一个共同父类 ContainerBase,父类定义了 initInternal和 startInternal 的通用处理内容,具体容器添加各自内容。
  • 最顶层容器的 init 是被 Service 调用,其他子容器的init方法 在执行 start 方法的时候通过状态 来判断是否调用。
  • start方法除了在 父容器的 startInternal 方法中调用,还会在父容器添加子容器的 addChild 方法中调用,(由于 Context和Wrapper是动态添加的)。

ContainerBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Override
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}

@Override
protected synchronized void startInternal() throws LifecycleException {
// 如果有 Cluster和 Realm则调用其 start 方法
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}

// 调用所有子容器的 start方法
// Start our child containers, if any
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}

boolean fail = false;
for (Future<Void> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
fail = true;
}
}
if (fail) {
throw new LifecycleException(
sm.getString("containerBase.threadedStartFailed"));
}

// 启用管道,后面介绍到
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();

// 设置状态值
setState(LifecycleState.STARTING);

// Start our thread
// 启动后台线程
threadStart();
}

startInternal 方法做了 5件事:

  • 如果有 Cluster和 Realm则调用其 start 方法。
  • 调用所有子容器的 start方法。
  • 调用管道中Value的start方法
  • 启动完成后设置生命周期状态:STARTING。
  • 启用后台线程,定时处理一些事情。

Cluster:用于配置集群,在 server.xml 中有 参考配置(用于同步session)。

Realm:Tomcat的安全域,可以用来管理资源的访问权限。

StandardContxt

StandaEngine、StandardContext、StandaHost、StandardWrapper(Servlet),主要介绍StandardContext、其它的类似。

主要看 initInternal、startInternal方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
// org.apache.catalina.core.StandardContext
@Override
protected synchronized void startInternal() throws LifecycleException {

if(log.isDebugEnabled())
log.debug("Starting " + getBaseName());

// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}

setConfigured(false);
boolean ok = true;

// Currently this is effectively a NO-OP but needs to be called to
// ensure the NamingResources follows the correct lifecycle
if (namingResources != null) {
namingResources.start();
}

// Post work directory
postWorkDirectory();

// Add missing components as necessary
if (getResources() == null) { // (1) Required by Loader
if (log.isDebugEnabled())
log.debug("Configuring default Resources");

try {
setResources(new StandardRoot(this));
} catch (IllegalArgumentException e) {
log.error(sm.getString("standardContext.resourcesInit"), e);
ok = false;
}
}
if (ok) {
resourcesStart();
}

if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}

// An explicit cookie processor hasn't been specified; use the default
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}

// Initialize character set mapper
getCharsetMapper();

// Validate required extensions
boolean dependencyCheck = true;
try {
dependencyCheck = ExtensionValidator.validateApplication
(getResources(), this);
} catch (IOException ioe) {
log.error(sm.getString("standardContext.extensionValidationError"), ioe);
dependencyCheck = false;
}

if (!dependencyCheck) {
// do not make application available if dependency check fails
ok = false;
}

// Reading the "catalina.useNaming" environment variable
String useNamingProperty = System.getProperty("catalina.useNaming");
if ((useNamingProperty != null)
&& (useNamingProperty.equals("false"))) {
useNaming = false;
}

if (ok && isUseNaming()) {
if (getNamingContextListener() == null) {
NamingContextListener ncl = new NamingContextListener();
ncl.setName(getNamingContextName());
ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
addLifecycleListener(ncl);
setNamingContextListener(ncl);
}
}

// Standard container startup
if (log.isDebugEnabled())
log.debug("Processing standard container startup");


// Binding thread
ClassLoader oldCCL = bindThread();

try {
if (ok) {
// Start our subordinate components, if any
Loader loader = getLoader();
if (loader instanceof Lifecycle) {
((Lifecycle) loader).start();
}

// since the loader just started, the webapp classloader is now
// created.
setClassLoaderProperty("clearReferencesRmiTargets",
getClearReferencesRmiTargets());
setClassLoaderProperty("clearReferencesStopThreads",
getClearReferencesStopThreads());
setClassLoaderProperty("clearReferencesStopTimerThreads",
getClearReferencesStopTimerThreads());
setClassLoaderProperty("clearReferencesHttpClientKeepAliveThread",
getClearReferencesHttpClientKeepAliveThread());
setClassLoaderProperty("clearReferencesObjectStreamClassCaches",
getClearReferencesObjectStreamClassCaches());

// By calling unbindThread and bindThread in a row, we setup the
// current Thread CCL to be the webapp classloader
unbindThread(oldCCL);
oldCCL = bindThread();

// Initialize logger again. Other components might have used it
// too early, so it should be reset.
logger = null;
getLogger();

Realm realm = getRealmInternal();
if(null != realm) {
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}

// Place the CredentialHandler into the ServletContext so
// applications can have access to it. Wrap it in a "safe"
// handler so application's can't modify it.
CredentialHandler safeHandler = new CredentialHandler() {
@Override
public boolean matches(String inputCredentials, String storedCredentials) {
return getRealmInternal().getCredentialHandler().matches(inputCredentials, storedCredentials);
}

@Override
public String mutate(String inputCredentials) {
return getRealmInternal().getCredentialHandler().mutate(inputCredentials);
}
};
context.setAttribute(Globals.CREDENTIAL_HANDLER, safeHandler);
}

// Notify our interested LifecycleListeners
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

// Start our child containers, if not already started
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}

// Start the Valves in our pipeline (including the basic),
// if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}

// Acquire clustered manager
Manager contextManager = null;
Manager manager = getManager();
if (manager == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.cluster.noManager",
Boolean.valueOf((getCluster() != null)),
Boolean.valueOf(distributable)));
}
if ( (getCluster() != null) && distributable) {
try {
contextManager = getCluster().createManager(getName());
} catch (Exception ex) {
log.error("standardContext.clusterFail", ex);
ok = false;
}
} else {
contextManager = new StandardManager();
}
}

// Configure default manager if none was specified
if (contextManager != null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("standardContext.manager",
contextManager.getClass().getName()));
}
setManager(contextManager);
}

if (manager!=null && (getCluster() != null) && distributable) {
//let the cluster know that there is a context that is distributable
//and that it has its own manager
getCluster().registerManager(manager);
}
}

if (!getConfigured()) {
log.error(sm.getString("standardContext.configurationFail"));
ok = false;
}

// We put the resources into the servlet context
if (ok)
getServletContext().setAttribute
(Globals.RESOURCES_ATTR, getResources());

if (ok ) {
if (getInstanceManager() == null) {
javax.naming.Context context = null;
if (isUseNaming() && getNamingContextListener() != null) {
context = getNamingContextListener().getEnvContext();
}
Map<String, Map<String, String>> injectionMap = buildInjectionMap(
getIgnoreAnnotations() ? new NamingResourcesImpl(): getNamingResources());
setInstanceManager(new DefaultInstanceManager(context,
injectionMap, this, this.getClass().getClassLoader()));
}
getServletContext().setAttribute(
InstanceManager.class.getName(), getInstanceManager());
InstanceManagerBindings.bind(getLoader().getClassLoader(), getInstanceManager());
}

// Create context attributes that will be required
if (ok) {
getServletContext().setAttribute(
JarScanner.class.getName(), getJarScanner());
}

// Set up the context init params
mergeParameters();

// Call ServletContainerInitializers
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}

// 回调 Listener
// Configure and call application event listeners
if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}

// Check constraints for uncovered HTTP methods
// Needs to be after SCIs and listeners as they may programmatically
// change constraints
if (ok) {
checkConstraintsForUncoveredMethods(findConstraints());
}

try {
// Start manager
Manager manager = getManager();
if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
} catch(Exception e) {
log.error(sm.getString("standardContext.managerFail"), e);
ok = false;
}

// Configure and call application filters
// 回调 we.xml 中定义的 filter
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}

// 调用 web.xml 中定义的 Servlet,如果设置了 loadOnStartUp
// Load and initialize all "load on startup" servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}

// Start ContainerBackgroundProcessor thread
super.threadStart();
} finally {
// Unbinding thread
unbindThread(oldCCL);
}

// Set available status depending upon startup success
if (ok) {
if (log.isDebugEnabled())
log.debug("Starting completed");
} else {
log.error(sm.getString("standardContext.startFailed", getName()));
}

startTime=System.currentTimeMillis();

// Send j2ee.state.running notification
if (ok && (this.getObjectName() != null)) {
Notification notification =
new Notification("j2ee.state.running", this.getObjectName(),
sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}

// The WebResources implementation caches references to JAR files. On
// some platforms these references may lock the JAR files. Since web
// application start is likely to have read from lots of JARs, trigger
// a clean-up now.
getResources().gc();

// Reinitializing if something went wrong
if (!ok) {
setState(LifecycleState.FAILED);
} else {
setState(LifecycleState.STARTING);
}
}

Pipeline-Value管道

Container处理请求是使用 Pipeline-Value管道来处理。

处理模式

Pipeline-Value是一种 责任链模式,它和普通责任链模式有两点区别:

  • 每个Pipeline都是有特定的Value,而且是在管道的最后一个执行,这个Value叫BaseValue,并且BaseValue是不可删除的;
  • 在上层容器的管道的BaseValue中会调用下层容器的管道。

一共有4种BaseValue,对应Container的4个组件:

  • StandardEngineValue
  • StandardHostValue
  • StandardContextValue
  • StandardWrapperValue

执行流程图:

实现方式

生命周期的实现方式

​ Pipeline在抽象类 ContainerBase 中定义,并在生命周期 startInternal、stopInternal、destoryInternal方法中调用管道的相应生命周期方法(管道不需要初始化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// org.apache.catalina.core.StandardPipeline
@Override
protected void initInternal() {
// NOOP
}

@Override
protected synchronized void startInternal() throws LifecycleException {

// Start the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}

setState(LifecycleState.STARTING);
}

@Override
protected synchronized void stopInternal() throws LifecycleException {

setState(LifecycleState.STOPPING);

// Stop the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).stop();
current = current.getNext();
}
}


@Override
protected void destroyInternal() {
Valve[] valves = getValves();
for (Valve valve : valves) {
removeValve(valve);
}
}

处理请求的实现方式

​ Pipeline调用所包含Value的invoke方法来处理请求,并且在BaseValue里又调用子容器Pipeline所包含Value的 invoke 方法,直到最后调用 Wrapper 的Pipeline所包含的 BaseValue—-StandardWrapperValue。

​ Connector 在收到请求后悔调用最顶层容器的 Pipeline 来处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// org.apache.catalina.core.StandardEngineValve
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {

// Select the Host to be used for this Request
// 事先处理好了,其它各容器一样
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}

// Ask this Host to process this request
// 调用下一个容器的 invoke 方法
host.getPipeline().getFirst().invoke(request, response);

}

Connector

​ Connector用于接收请求并将请求封装成Request和Response来具体处理。最底层使用的是 Socket。Request和Response封装后交给Container(Servlet的容器)处理请求,Container处理后返回给Connector,最后由Socket返回给客户端。

结构

​ Connector中具体是用 ProtocolHandler处理请求的,代表不同的连接类型。Http11Protocol使用普通Socket连接,Http11NioProtocol使用NioSocket连接。

​ ProtocolHandler中有三个重要的组件:

  • Endpoint:处理底层的Socket网络连接;
  • Processor:将Endpoint接收的Socket封装成Request;
  • Adapter:将封装后的Request交给Container处理;

​ Endpoint的抽象类 AbstractEndpoint定义了 Acceptor和 AsyncTimeout两个内部类 和 Handler接口。

  • Acceptor:监听请求;
  • AsyncTimeout:检查异步Request请求的超时;
  • Handler:处理接收的Socket,内部调用 Processor进行处理;

Connector类

在server.xml 定义的 Connector, protocol属性会设置到 Connector构造函数中的参数中:

1
2
3
4
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public Connector(String protocol) {
// 根据 protocol 设置 protocolHandlerClassName 的值。
setProtocol(protocol);
// Instantiate protocol handler
ProtocolHandler p = null;
try {
// 反射初始化 protocolHandlerClassName
Class<?> clazz = Class.forName(protocolHandlerClassName);
p = (ProtocolHandler) clazz.getConstructor().newInstance();
} finally {
// 赋值给 protocolHandler
this.protocolHandler = p;
}

// 设置编码集
if (Globals.STRICT_SERVLET_COMPLIANCE) {
uriCharset = StandardCharsets.ISO_8859_1;
} else {
uriCharset = StandardCharsets.UTF_8;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize adapter
// 初始化 Adapter 并绑定到 protocolHandler 上,
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// .......... 省略部分代码
try {
protocolHandler.init();
}
}

@Override
protected void startInternal() throws LifecycleException {

// 验证 Connector 端口号
// Validate settings before starting
if (getPort() < 0) { throw new LifecycleException(); }
// 设置状态值
setState(LifecycleState.STARTING);
try {
protocolHandler.start();
}
}

ProtocolHandler类

ProtocolHandler 接口下有一个抽象实现类 AbstractProtocol 类,它下面有 2种类型:

  • Ajp:Apache JServ Protocol ,Apache的定向包协议,主要用于与前端服务器进行通信,是一个长连接,节省开销。
  • Http

在 server.xml 中 Connector 默认 使用 HTTP/1.1,即:org.apache.coyote.http11.Http11NioProtocol

public Http11NioProtocol() {
    // 创建 NioEndpoint
    super(new NioEndpoint());
}

public AbstractHttp11Protocol(AbstractEndpoint<S> endpoint) {
    super(endpoint);
    // 设置超时时间 :60000
    setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
    ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
    setHandler(cHandler);
    // Endpoint 绑定 handler
    getEndpoint().setHandler(cHandler);
}

public AbstractProtocol(AbstractEndpoint<S> endpoint) {
    this.endpoint = endpoint;
    setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
    setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}

Endpoint

父类(org.apache.tomcat.util.net.AbstractEndpoint)

​ Endpoint用于处理具体链接和传输数据,在 NioEndpoint中 的 Poller和SocketProcessor内部类,

init、start 方法定义在 父类中,先看下父类 相应方法:

// org.apache.tomcat.util.net.AbstractEndpoint
public void init() throws Exception {
    if (bindOnInit) {
        // 由 子类 NioEndpoint 具体实现
        bind();
        bindState = BindState.BOUND_ON_INIT;
    }
}

public final void start() throws Exception {
    if (bindState == BindState.UNBOUND) {
        bind();
        bindState = BindState.BOUND_ON_START;
    }
    // 由 子类 NioEndpoint 具体实现
    startInternal();
}

子类实现的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// org.apache.tomcat.util.net.NioEndpoint
@Override
public void bind() throws Exception {

serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.socket().bind(addr,getAcceptCount());
serverSock.configureBlocking(true); //mimic APR behavior

// Initialize thread count defaults for acceptor, poller
// 初始化启动 acceptor线程个数
if (acceptorThreadCount == 0) {
// FIXME: Doesn't seem to work that well with multiple accept threads
acceptorThreadCount = 1;
}
// 初始化启动 poller线程个数
if (pollerThreadCount <= 0) {
//minimum one poller thread
pollerThreadCount = 1;
}
setStopLatch(new CountDownLatch(pollerThreadCount));

// Initialize SSL if needed
// 是否需要初始化 ssl
initialiseSsl();

selectorPool.open();
}

/**
* Start the NIO endpoint, creating acceptor, poller threads.
*/
@Override
public void startInternal() throws Exception {

if (!running) {
running = true;
paused = false;

processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());

// Create worker collection
// 创建 Executor
if ( getExecutor() == null ) {
createExecutor();
}

initializeConnectionLatch();

// Start poller threads
// 启动 poller 线程, 进入此类。
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i<pollers.length; i++) {
pollers[i] = new Poller();
Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}

// 调用父类中方法
startAcceptorThreads();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Poller 是 NioEndpoint 的一个 内部类
public class Poller implements Runnable {
private Selector selector;
private final SynchronizedQueue<PollerEvent> events =
new SynchronizedQueue<>();
private volatile boolean close = false;
private long nextExpiration = 0;//optimize expiration handling
private AtomicLong wakeupCounter = new AtomicLong(0);
private volatile int keyCount = 0;
public Poller() throws IOException {
this.selector = Selector.open();
}
/**
* The background thread that adds sockets to the Poller, checks the
* poller for triggered events and hands the associated socket off to an
* appropriate processor as events occur.
*/
@Override
public void run() {
// Loop until destroy() is called
while (true) {

boolean hasEvents = false;

try {
if (!close) {
hasEvents = events();
if (wakeupCounter.getAndSet(-1) > 0) {
//if we are here, means we have other stuff to do
//do a non blocking select
keyCount = selector.selectNow();
} else {
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
events();
timeout(0, false);
try {
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error("",x);
continue;
}
//either we timed out or we woke up, process events first
if ( keyCount == 0 ) hasEvents = (hasEvents | events());

Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (attachment == null) {
iterator.remove();
} else {
iterator.remove();
processKey(sk, attachment);
}
}//while
//process timeouts
timeout(keyCount,hasEvents);
}//while
getStopLatch().countDown();
}

protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
if (sk.isReadable()) {
// 父类中实现
if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
closeSocket = true;
}
}
if (!closeSocket && sk.isWritable()) {
if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
closeSocket = true;
}
}
}

// AbstractEndpoint 类
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
SocketProcessorBase<S> sc = processorCache.pop();
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
Executor executor = getExecutor();
if (dispatch && executor != null) {
executor.execute(sc);
} else {
// 进入次方法
sc.run();
}
}
return true;
}

// SocketProcessorBase
public final void run() {
// 会在 NioEndpoint 中实现
doRun();
}

/**
* 此方法 调用 Handler 转交给 Processor 去处理 process 方法,
* 然后再 转交给 Adapter类,处理 service方法,完成整个流程。
*/
protected void doRun() {
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
if (event == null) {
// process 方法,进入
state = getHandler()
.process(socketWrapper, SocketEvent.OPEN_READ);
} else {
state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
close(socket, key);
}
} else if (handshake == -1 ) {
close(socket, key);
}
}
1
2
3
4
5
6
7
// AbstractProtocol
@Override
public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
do {
state = processor.process(wrapper, status);
}
}

Processor

UpgradeProcessorBase:用于处理 WebSocket协议;

AbstractProcessor:用于处理正常协议;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// AbstractProcessorLight(公共父类)
@Override
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status) {
SocketState state = SocketState.CLOSED;
Iterator<DispatchType> dispatches = null;
do {
if (dispatches != null) {
DispatchType nextDispatch = dispatches.next();
state = dispatch(nextDispatch.getSocketStatus());
} else if (status == SocketEvent.DISCONNECT) {
// Do nothing here, just wait for it to get recycled
} else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
state = dispatch(status);
if (state == SocketState.OPEN) {
// There may be pipe-lined data to read. If the data isn't
// processed now, execution will exit this loop and call
// release() which will recycle the processor (and input
// buffer) deleting any pipe-lined data. To avoid this,
// process it now.
// 进入此 service 方法
state = service(socketWrapper);
}
} else if (status == SocketEvent.OPEN_WRITE) {
// Extra write event likely after async, ignore
state = SocketState.LONG;
} else if (status == SocketEvent.OPEN_READ){
state = service(socketWrapper);
} else {
// Default to closing the socket if the SocketEvent passed in
// is not consistent with the current state of the Processor
state = SocketState.CLOSED;
}
if (state != SocketState.CLOSED && isAsync()) {
state = asyncPostProcess();
}
} while (state == SocketState.ASYNC_END ||
dispatches != null && state != SocketState.CLOSED);
return state;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 此处 贴的 Http11Processor 类的代码。
@Override
public SocketState service(SocketWrapperBase<?> socketWrapper) {
// 省略部分代码
// Process the request in the adapter
if (!getErrorState().isError()) {
try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
// getAdapter 找个 适配器,调用具体的 service 方法,下个 Adapter 说明
getAdapter().service(request, response);
// Handle when the response was committed before a serious
// error occurred. Throwing a ServletException should both
// set the status to 500 and set the errorException.
// If we fail here, then the response is likely already
// committed, so we can't try and set headers.
if(keepAlive && !getErrorState().isError() && !isAsync() &&
statusDropsConnection(response.getStatus())) {
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
}
}

// Finish the handling of the request
rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
if (!isAsync()) {
// If this is an async request then the request ends when it has
// been completed. The AsyncContext is responsible for calling
// endRequest() in that case.
endRequest();
}
}

Adapter

​ Adapter 只有一个实现类。由 Process 来调用 service ,在Processor 中贴的代码写到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// getAdapter().service(request, response);
// Adapter 只有一个实现类,CoyoteAdapter,见下面 service 方法

// org.apache.catalina.connector.CoyoteAdapter
@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) {
// 此处省略部分代码:

// Parse and set Catalina and configuration specific
// request parameters
// 处理请求头等信息
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported(
connector.getService()
.getContainer()
.getPipeline()
.isAsyncSupported());

// 此处调用 Pipeline Value 的 invoke 方法。(Engine是最顶层容器)
// 关于 Pipeline 的执行过程 见上面的流程图,最后会执行 StandardWrapperValve
// 的 invoke 方法,其中会有 调用
// doFilter --> servlet.service(request, response); 此处会转交给
// SpringMVC 的 DispatchServlet 类 进行处理。
connector.getService()
.getContainer()
.getPipeline()
.getFirst()
.invoke(request, response);
}

// Recycle the wrapper request and response
// 回收 Request 和 Response
if (!async) {
request.recycle();
response.recycle();
}
}

Service方法的调用

再回过头看下Pipeline-Value如何请求到的 Servlet#servic方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// org.apache.catalina.core.StandardWrapperValue
/**
* Invoke the servlet we are managing, respecting the rules regarding
* servlet lifecycle and SingleThreadModel support.
* @param request Request to be processed
* @param response Response to be produced
*/
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {

// 分配 servlet 实例处理此次请求
if (!unavailable) {
servlet = wrapper.allocate();
}

// 创建 Filter
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
if ((servlet != null) && (filterChain != null)) {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
// 此处 调用 service 方法
filterChain.doFilter(request.getRequest(),
response.getResponse());
}
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter
(request.getRequest(), response.getResponse());
}
}

// Release the filter chain (if any) for this request
if (filterChain != null) {
filterChain.release();
}

// 释放 instance
if (servlet != null) {
wrapper.deallocate(servlet);
}

// 如果不可用,卸载 & 释放 instance
if ((servlet != null) &&
(wrapper.getAvailable() == Long.MAX_VALUE)) {
wrapper.unload();
}
}

@Override
public void doFilter(ServletRequest request, ServletResponse response) {
// 省略部分代码
internalDoFilter(request,response);
}

private void internalDoFilter(ServletRequest request,
ServletResponse response) {
// 省略部分代码
// We fell off the end of the chain -- call the servlet instance
servlet.service(request, response);
}

说明:

​ 该 servlet 即 在 web.xml 中配置的 实现了 Servlet 接口的 DispatchServlet。此处会转交给 SpringMVC 进行相应的流程处理。SpringMVC 的处理流程后续再看。

Web.xml 解析

​ 在说到 Lifecycle时,提到 这个接口中定义了 13个 状态常量值,tomcat的启动 依赖于这些状态值。

通过判断 这个状态值,进行 web.xml 的解析操作。(会省略部分代码)

​ 在Tomcat/conf/ 下有 context.xml、web.xml、server.xml。

// org.apache.catalina.util.LifecycleBase
protected synchronized void setState(LifecycleState state, Object data) {
    setStateInternal(state, data, true);
}
private synchronized void setStateInternal(LifecycleState state,
            Object data, boolean check) throws LifecycleException {
    this.state = state;
    String lifecycleEvent = state.getLifecycleEvent();
    if (lifecycleEvent != null) {
        fireLifecycleEvent(lifecycleEvent, data);
    }
}
protected void fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event = new LifecycleEvent(this, type, data);
    for (LifecycleListener listener : lifecycleListeners) {
        listener.lifecycleEvent(event);
    }
}

// 进入到  org.apache.catalina.startup.ContextConfig
@Override
public void lifecycleEvent(LifecycleEvent event) {
    // Process the event that has occurred
    if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
        // 此处 
        configureStart();
    } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
        beforeStart();
    } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
        // Restore docBase for management tools
        if (originalDocBase != null) {
            context.setDocBase(originalDocBase);
        }
    } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
        configureStop();
    } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
        init();
    } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
        destroy();
    }
}

protected synchronized void configureStart() {
    webConfig();
}

/**
 * Scan the web.xml files that apply to the web application and merge them
 * using the rules defined in the spec. For the global web.xml files,
 * where there is duplicate configuration, the most specific level wins. ie
 * an application's web.xml takes precedence over the host level or global
 * web.xml file.
 */
protected void webConfig() {
    WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
                                                 context.getXmlValidation(), context.getXmlBlockExternal());

    Set<WebXml> defaults = new HashSet<>();
    // 会加载GlobalWebXml、HostWebXml
    defaults.add(getDefaultWebXmlFragment(webXmlParser));

    WebXml webXml = createWebXml();

    // Parse context level web.xml
    InputSource contextWebXml = getContextWebXmlSource();
    if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
        ok = false;
    }

    ServletContext sContext = context.getServletContext();

    // Ordering is important here

    // Step 1. Identify all the JARs packaged with the application and those
    // provided by the container. If any of the application JARs have a
    // web-fragment.xml it will be parsed at this point. web-fragment.xml
    // files are ignored for container provided JARs.
    Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);

    // Step 2. Order the fragments.
    Set<WebXml> orderedFragments = null;
    orderedFragments =
        WebXml.orderWebFragments(webXml, fragments, sContext);

    // Step 3. Look for ServletContainerInitializer implementations
    if (ok) {
        processServletContainerInitializers();
    }

    if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
        // Step 4. Process /WEB-INF/classes for annotations and
        // @HandlesTypes matches
        Map<String,JavaClassCacheEntry> javaClassCache = new HashMap<>();

        if (ok) {
            WebResource[] webResources =
                context.getResources().listResources("/WEB-INF/classes");

            for (WebResource webResource : webResources) {
                // Skip the META-INF directory from any JARs that have been
                // expanded in to WEB-INF/classes (sometimes IDEs do this).
                if ("META-INF".equals(webResource.getName())) {
                    continue;
                }
                processAnnotationsWebResource(webResource, webXml,
                                              webXml.isMetadataComplete(), javaClassCache);
            }
        }

        // Step 5. Process JARs for annotations and
        // @HandlesTypes matches - only need to process those fragments we
        // are going to use (remember orderedFragments includes any
        // container fragments)
        if (ok) {
            processAnnotations(
                orderedFragments, webXml.isMetadataComplete(), javaClassCache);
        }

        // Cache, if used, is no longer required so clear it
        javaClassCache.clear();
    }

    if (!webXml.isMetadataComplete()) {
        // Step 6. Merge web-fragment.xml files into the main web.xml
        // file.
        if (ok) {
            ok = webXml.merge(orderedFragments);
        }

        // Step 7. Apply global defaults
        // Have to merge defaults before JSP conversion since defaults
        // provide JSP servlet definition.
        webXml.merge(defaults);

        // Step 8. Convert explicitly mentioned jsps to servlets
        if (ok) {
            convertJsps(webXml);
        }

        // Step 9. Apply merged web.xml to Context
        if (ok) {
            configureContext(webXml);
        }
    } else {
        webXml.merge(defaults);
        convertJsps(webXml);
// 此处 方法 较长,可查看源码
// 处理 context-param、filter、servlet、servlet-mapping等
        configureContext(webXml);
    }

    if (context.getLogEffectiveWebXml()) {
        log.info("web.xml:\n" + webXml.toXml());
    }

    // Always need to look for static resources
    // Step 10. Look for static resources packaged in JARs
    if (ok) {
        // Spec does not define an order.
        // Use ordered JARs followed by remaining JARs
        Set<WebXml> resourceJars = new LinkedHashSet<>();
        for (WebXml fragment : orderedFragments) {
            resourceJars.add(fragment);
        }
        for (WebXml fragment : fragments.values()) {
            if (!resourceJars.contains(fragment)) {
                resourceJars.add(fragment);
            }
        }
        processResourceJARs(resourceJars);
        // See also StandardContext.resourcesStart() for
        // WEB-INF/classes/META-INF/resources configuration
    }

    // Step 11. Apply the ServletContainerInitializer config to the
    // context
    if (ok) {
        for (Map.Entry<ServletContainerInitializer,
             Set<Class<?>>> entry :
             initializerClassMap.entrySet()) {
            if (entry.getValue().isEmpty()) {
                context.addServletContainerInitializer(
                    entry.getKey(), null);
            } else {
                context.addServletContainerInitializer(
                    entry.getKey(), entry.getValue());
            }
        }
    }
}
CATALOG
  1. 1. Tomcat
    1. 1.1. 整体架构图
    2. 1.2. Tomcat启动类
      1. 1.2.1. Main方法
    3. 1.3. Catalina类
      1. 1.3.1. Await方法
      2. 1.3.2. Load方法
        1. 1.3.2.1. Init方法
      3. 1.3.3. Start方法
      4. 1.3.4. Stop方法
    4. 1.4. 启动流程
    5. 1.5. Tomcat的生命周期
      1. 1.5.1. Lifecycle接口
      2. 1.5.2. LifecyleBase
    6. 1.6. Container
      1. 1.6.1. 4个容器的作用:
      2. 1.6.2. 4个容器的配置方法
      3. 1.6.3. Container的启动
        1. 1.6.3.1. ContainerBase
        2. 1.6.3.2. StandardContxt
    7. 1.7. Pipeline-Value管道
      1. 1.7.1. 处理模式
      2. 1.7.2. 实现方式
        1. 1.7.2.1. 生命周期的实现方式
        2. 1.7.2.2. 处理请求的实现方式
    8. 1.8. Connector
      1. 1.8.1. 结构
      2. 1.8.2. Connector类
      3. 1.8.3. ProtocolHandler类
        1. 1.8.3.1. Endpoint
        2. 1.8.3.2. Processor
        3. 1.8.3.3. Adapter
    9. 1.9. Service方法的调用
    10. 1.10. Web.xml 解析