之前一直都是只会用tomcat,也只是知道这是个调用各种各样bean的容器,但是却有不少疑问,比如一个http请求如何从客户端到最终处理的servlet手里。今天下了源码来初步学习下,以下为笔记,基于tomcat7.0.4的源码。
当tomcat启动时,调用catalina.bat,然后调用org.apache.catalina.startup.Bootstrap中的main(),再到catalina.java中的load()。
如下是load函数一部分:
Digester digester = createStartDigester();
file = configFile();
inputStream = new FileInputStream(file);
digester.parse(inputSource);//解析conf/server.xml文件
getServer().init();
在server.xml中,有如下结构:
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
..............
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<!--The connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<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" />
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
</Service>
</Server>
createStartDigester()中初始化了StandardServer/StandardService/StandardThreadExecutor/Connector等对象。
protected Digester createStartDigester() {
.........
// Configure the actions we will be using
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
........
}
当Server起来后,StandardService就会被start,如下:
protected void startInternal() throws LifecycleException {
// Start our defined Container first
if (container != null) {
synchronized (container) {
container.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
// Start our defined Connectors second
synchronized (connectors) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}
Service会启动它的连接器和容器,一个Service包含一个容器和多个连接器。
由于server.xml中定义的协议不同,连接器一般用Http11Protocol或者AjpProtocol。
在Http11Protocol的构造函数中
public Http11Protocol() {
endpoint = new JIoEndpoint();//初始化JIoEndpoint
cHandler = new Http11ConnectionHandler(this);//指定处理每个请求的Handler
((JIoEndpoint) endpoint).setHandler(cHandler);
}
会遇到JIoEndpoint这个东西,这是个处理socket请求的类。在他的类的注释中
This class implement a simple server model: one listener thread accepts on a socket and creates a new worker thread for each incoming connection.
说明了他的作用是接受客户端请求并且为每个请求单独建立新的线程来处理。
其中包含了三个子类:SocketProcessor(实际处理每个请求)、Acceptor(分发每个请求)、AsyncTimeout(看起来是超时机制)
我们来看下JIoEndpoint.startInternal()函数:
createExecutor();//建立一个线程池,可以定义最大并发数量
startAcceptorThreads();//建立一个监听线程,具体线程类为Acceptor
bind。。。。listen。。。
当用户发起请求后,Acceptor中的processSocket()会处理这个请求,调用线程池中的空闲资源:getExecutor().execute(new SocketProcessor(wrapper)); 由Http11Connector的构造函数中可以看到,具体的请求handler是Http11ConnectionHandler类。
鉴于后面都是按照debug的断点一步步的跟踪了,所以就不写了,总之最后就是会new到具体的cmdServlet对象并调用到具体的处理http请求的servlet。