tomcat7源码分析
本篇分为六个部分:
tomcat基本框架
tomcat启动流程简介
tomcat启动流程源码分析
tomcat处理一个请求过程分析
.jsp生成.java和.class流程分析
apache beachmark性能测试
一、tomcat基本框架
由三部分组成:
组件架构:组件搭起房梁
基于JMX:JMX管理组件等对象
事件侦听:通过事件变更状态
- 组件架构结构:
1.1 结构图:
组件简述:
Catalina:与开始/关闭shell脚本交互的主类,因此如果要研究启动和关闭的过程,就从这个类开始看起。
Server:是整个Tomcat组件的容器,包含一个或多个Service。
Service:Service是包含Connector和Container的集合,Service用适当的Connector接收用户的请求,再发给相应的Container来处理。
Connector:实现某一协议的连接器,如默认的有实现HTTP、HTTPS、AJP协议的。
Container:可以理解为处理某类型请求的容器,处理的方式一般为把处理请求的处理器包装为Valve对象,并按一定顺序放入类型为Pipeline的管道里。Container有多种子类型:Engine、Host、Context和Wrapper,这几种子类型Container依次包含,处理不同粒度的请求。另外Container里包含一些基础服务,如Loader、Manager和Realm。
Engine:Engine包含Host和Context,接到请求后仍给相应的Host在相应的Context里处理。
Host:就是我们所理解的虚拟主机。
Context:就是我们所部属的具体Web应用的上下文,说白了就是我们的应用,每个请求都在是相应的上下文里处理的。
Wrapper:Wrapper是针对每个Servlet的Container,每个Servlet都有相应的Wrapper来管理。
可以看出Server、Service、Connector、Container、Engine、Host、Context和Wrapper这些核心组件的作用范围是逐层递减,并逐层包含。
下面就是些被Container所用的基础组件:
Loader:是被Container用来载入各种所需的Class。
Manager:是被Container用来管理Session池。
Realm:是用来处理安全里授权与认证。
1.2 组件关系
service直接包含connector(可多个)和containtor(一个),这好比一个家庭,男主外,女主内。service是房子,connector是男子,主要工作是对外;Container是女子,处理家里内部事务。connector和service一起组件一个家庭,住在service这个房子里
- JMX管理组件
Tomcat会为每个组件进行注册过程,通过Registry管理起来,而Registry是基于JMX来实现的,因此在看组件的init和start过程实际上就是初始化MBean和触发MBean的start方法,会大量看到形如:
Registry.getRegistry(null, null).invoke(mbeans, "init", false);
Registry.getRegistry(null, null).invoke(mbeans, "start", false);
这样的代码,这实际上就是通过JMX管理各种组件的行为和生命期。
- 事件侦听
各个组件在其生命期中会有各种各样行为,而这些行为都有触发相应的事件,Tomcat就是通过侦听这些时间达到对这些行为进行扩展的目的。在看组件的init和start过程中会看到大量如:
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);这样的代码,这就是对某一类型事件的触发,如果你想在其中加入自己的行为,就只用注册相应类型的事件即可。
二、tomcat启动流程简介
启动时主要流程如下:
1. 初始化类加载器:主要初始化Tomcat加载自身类库的StandardClassLoader(commonClassLoader等)。
解析conf/serverl.xml配置文件:使用Digester解析Tomcat的server.xml(SAXReader),初始化各个组件(包含各个web应用,解析对应的web.xml进行初始化)。
初始化容器:初始化Service的各级容器Container,包括我们Web应用(我们熟悉的Listener,Filter,Servlet等初始化等在这里完成)。
初始化Connector:初始化Service的的Connector
初始化后,等待请求的到来
时序图
从上图可以看出,Tomcat启动分为init和start两个过程,核心组件都实现了Lifecycle接口,都需实现start方法,因此在start过程中就是从Server开始逐层调用子组件的start过程。
下面我们就根据时序图结合源码进行分析
三、tomcat7启动流程源码分析
从catalina.bat找到启动开始的地方:Bootstrap类,它中有个main方法
set _EXECJAVA=%_RUNJAVA%
set MAINCLASS=org.apache.catalina.startup.Bootstrap
set ACTION=start
set SECURITY_POLICY_FILE=
set DEBUG_OPTS=
set JPDA=
Bootstrap.main开始
public static void main(String args[]) {
System.out.println("-- my test starting --");
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
//set一些系统属性、初始化classLoader和Catalina.setParentClassLoader()
bootstrap.init()
} catch (Throwable t) {}
daemon = bootstrap;
}
String command = "start";
if (command.equals("start")) {
daemon.setAwait(true);
//加载配置资源,通过反射调用catalina.load方法,利用catalina.load方法创建digester实例,从而解析conf/server.xml文件
daemon.load(args);
//运行各个组件,容器开始启动
daemon.start();
}
}
从代码可以看出,main方法主要做三件事:
init、load、start
init: 初始化类加载器
load:加载conf/server.xml,根据规则解析元素为对应对象,并set属性,元素间关联关系,且变更各个对象生命周期状态
start:启动各个组件
调用Bootstrap.init()
主要做了两件事:
1. 设置catalinahome和catalinbase(即java的vm options的值:”E:\tomcat_test\apache-tomcat-7.0.73-src\lunch”
2. 实例化**ClassLoaders
public void init() throws Exception {
// Set Catalina path
setCatalinaHome();
setCatalinaBase();
//实例化**ClassLoaders,创建URLClassLoader
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
Class<?> startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
catalinaDaemon = startupInstance;
}
调用Bootstrap.load(args)
通过反射实例化org.apache.catalina.startup.Catalina,并调用catalina.load(),在load方法中做了下面的事:
1. Digester类按照预定的规则解析server.xml,元素转化为对象,包括构造对象,set属性到对象字段,同时维护相互关联关系
2. 调用Server.init()方法,这其中会一连串的发布事件病调用各个service.init()方法,完成各个组件的初始化,各个组件包括:StandardServer、StandardService、StandardEngine、StandardHost、StandardContext、StandardWrapper。从StandardServer.init()到StandardWrapper.init()演示了这一过程
Catalina.load()
public void load() {
// Create and execute our Digester
//预定义了解析规则
Digester digester = createStartDigester();
try {
// 获取conf/server.xml文件
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
// 解析conf/server.xml文件(SAX),并根据规则初始化各个组件实例和关联关系:如server元素实例化standardServer实例对象,把属性内容set到对应实例中
digester.parse(inputSource);
} catch (SAXParseException spe) {
}
// server关联catalina,在解析server.xml过程中,会发现父子元素对应实例的相互关联性(双向性)
getServer().setCatalina(this);
// Start the new server
try {
// 调用各个组件的init方法,初始化各个组件
getServer().init();
} catch (LifecycleException e) {
}
}
StandardServer.init()
上述各个组件有个共同的父类:LifecycleBase,用于管理组件的生命周期和状态变化。在走Server.init()前,先走父类LifecycleBase.init()方法,事件驱动当前组件状态的变化
LifecycleBase.init()
变更状态(通过事件变更),进入子类initInternal()方法
public final synchronized void init() throws LifecycleException {
try {
//变更生命周期状态(通过事件变更)
setStateInternal(LifecycleState.INITIALIZING, null, false);
// 进入子类的initInternal()方法
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {}
}
从StandardServer到StandardWrapper,都会调用initInternal(),并伴随调用共有父类LifecycleBase.init()
StandardServer.init()方法
核心代码功能:
1. 注册mbean
2. 调用service.init()方法,同样,先走LifecycleBase.init()
protected void initInternal() throws LifecycleException {
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
globalNamingResources.init();
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
service.initIntern