Tomcat学习2:一键启动以及源码阅读
一次请求在Tomcat中经过的组件
Tomcat处理一个HTTP请求,在各组件中的流转过程如下图红色箭头:
一个系统通过如此多的组件组装起来完成一次完成的服务,那如何管理组件的创建、初始化和调用关系?
Lifecycle
系统设计要找到不变的点和变化的点,这里不变的地方就是每个组件都要创建、初始化、启动、销毁等,这些状态和状态之间的转化是不变的。变化的是每个组件初始化方法不一样。
Tomcat把不变的地方抽象出一个生命周期Lifecycle接口,定义了一些不变的方法:init,start,stop,destory,各组件去实现具体的逻辑。在父组件的init方法里面,会调用子组件的Init方法,只要调用最顶层的server组件的Init和start方法,整个tomcat组件都会被启动起来。(组合模式-Lifecycle接口)
生命周期会对应到一个个状态LiftcycleState,状态可作为事件,是可以被监听的。一个组件的状态变化会触发子组件的变化,比如Host容器的启动事件会触发Web应用的扫描和加载(反射)(观察者模式),最终会在Host容器中创建出Context容器,Lifecycle接口里有两个方法:添加监听器和删除监听器。
LifecycleBase抽象类实现了Lifecycle接口,并把一些公共的逻辑放到基类中,如生命状态的转变和、生命周期事件的触发等,子类就负责自己的初始化、启动和停止等方法(模板模式), 子类的实现会加上Internal后缀,比如InitInternal,startInternal等。
如何启动Tomcat
1.Tomcat本质上是一个Java程序,因此startup.sh
脚本会启动一个JVM来运行Tomcat的启动类Bootstrap。
2.Bootstrap的主要任务是初始化Tomcat的类加载器,并且创建Catalina。Tomcat为什么需要自己的类加载器?
3.Catalina是一个启动类,它通过解析server.xml
、创建相应的组件,并调用Server的start方法和init方法。
Catalina作为管理者,还通过”钩子“处理各种异常,如tomcat关闭时,如何释放资源以及内存数据刷到磁盘等
4.Server组件的职责就是管理Service组件,它会负责调用Service的start方法。
5.Service组件的职责就是管理连接器和顶层容器Engine,因此它会调用连接器和Engine的start方法。
1:Bootstrap类
Tomcat是通过startup.sh调用了Bootstra的main方法启动
1.1:main方法:
1 public static void main(String args[]) {
2
3 synchronized (daemonLock) {
4 if (daemon == null) {
5 // Don't set daemon until init() has completed
6 Bootstrap bootstrap = new Bootstrap();
7 try {
// 1:初始化
8 bootstrap.init();
9 } catch (Throwable t) {
13 }
14 daemon = bootstrap;
15 }
21 }
22
23 try {
24 String command = "start";
25 if (args.length > 0) {
26 command = args[args.length - 1];
27 }
28 // 2:对同的命令做不同的动作
29 if (command.equals("startd")) {
30 args[args.length - 1] = "start";
31 daemon.load(args);
32 daemon.start();
33 } else if (command.equals("stopd")) {
34 args[args.length - 1] = "stop";
35 daemon.stop();
36 }
63 }
64 }
主要做一些初始化init和完成一些动作指令load,start
1.2:init方法:
1 public void init() throws Exception {
2 //1:类加载器
3 initClassLoaders();
4
5 Thread.currentThread().setContextClassLoader(catalinaLoader);
6
7 SecurityClassLoad.securityClassLoad(catalinaLoader);
8
9 // Load our startup class and call its process() method
10 if (log.isDebugEnabled())
11 log.debug("Loading startup class");
12 Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
13 Object startupInstance = startupClass.getConstructor().newInstance();
14
15 // Set the shared extensions class loader
16 if (log.isDebugEnabled())
17 log.debug("Setting startup class properties");
18 String methodName = "setParentClassLoader";
19 Class<?> paramTypes[] = new Class[1];
20 paramTypes[0] = Class.forName("java.lang.ClassLoader");
21 Object paramValues[] = new Object[1];
22 paramValues[0] = sharedLoader;
//2:实例化catalina
23 Method method =
24 startupInstance.getClass().getMethod(methodName, paramTypes);
25 method.invoke(startupInstance, paramValues);
26
27 catalinaDaemon = startupInstance;
28 }
1:初始化类加载器,包括了common类加载器,shared类加载器,catalina类加载器(Tomcat类加载器和Jvm类加载器?),其中common类加载器作为父类加载器
2:实例化Catalina对象,并传入catalinaClassLoader作为parentClassLoader加载子组件,实现catalinaClassLoader和shareClassLoader隔离
1.3:load方法:
1 private void load(String[] arguments) throws Exception {
2
3 // Call the load() method
4 String methodName = "load";
5 Method method =
6 catalinaDaemon.getClass().getMethod(methodName, paramTypes);
7 method.invoke(catalinaDaemon, param);
8 }
通过反射调用Catalina类的load方法
1.4:start方法:
start方法也通过反射调用了Catalina类的start方法
2:Catalina
Catalina作为启动类,它通过解析server.xml,
创建相应的组件,并调用Server的start方法和init方法完成Tomcat的启动过程
2.1:load方法:
1 public void load() {
2 // Set configuration source
3 ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
4 File file = configFile();
5
6 // Create and execute our Digester
// 1:解析server.xml,创建各组件:server,service,ThreadPool,Listener等
7 Digester digester = createStartDigester();
8
9 try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
10 InputStream inputStream = resource.getInputStream();
11 InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
12 inputSource.setByteStream(inputStream);
13 digester.push(this);
14 digester.parse(inputSource);
15 } catch (Exception e) {
16 log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
17 if (file.exists() && !file.canRead()) {
18 log.warn(sm.getString("catalina.incorrectPermissions"));
19 }
20 return;
21 }
22
23 getServer().setCatalina(this);
24 getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
25 getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
26
27 // Stream redirection
28 initStreams();
29
30 // Start the new server
31 try {
// 2:调用server的init方法初始化Tomcat各组件
32 getServer().init();
33 } catch (LifecycleException e) {
34
35 }
36
37 }
1:对server.xml进行解析:
把Server、Service、Connector、Engine、Host各组件实例化,并维护父子级关系。其中createStartDigester方法如下:
1 protected Digester createStartDigester() { 2 long t1=System.currentTimeMillis(); 3 // Initialize the digester 4 Digester digester = new Digester(); 5 digester.setValidating(false); 6 digester.setRulesValidation(true); 7 Map<Class<?>, List<String>> fakeAttributes = new HashMap<>(); 8 // Ignore className on all elements 9 List<String> objectAttrs = new ArrayList<>(); 10 objectAttrs.add("className"); 11 fakeAttributes.put(Object.class, objectAttrs); 12 // Ignore attribute added by Eclipse for its internal tracking 13 List<String> contextAttrs = new ArrayList<>(); 14 contextAttrs.add("source"); 15 fakeAttributes.put(StandardContext.class, contextAttrs); 16 // Ignore Connector attribute used internally but set on Server 17 List<String> connectorAttrs = new ArrayList<>(); 18 connectorAttrs.add("portOffset"); 19 fakeAttributes.put(Connector.class, connectorAttrs); 20 digester.setFakeAttributes(fakeAttributes); 21 digester.setUseContextClassLoader(true); 22 23 // Configure the actions we will be using 24 digester.addObjectCreate("Server", 25 "org.apache.catalina.core.StandardServer", 26 "className"); 27 digester.addSetProperties("Server"); 28 digester.addSetNext("Server", 29 "setServer", 30 "org.apache.catalina.Server"); 31 32 digester.addObjectCreate("Server/GlobalNamingResources", 33 "org.apache.catalina.deploy.NamingResourcesImpl"); 34 digester.addSetProperties("Server/GlobalNamingResources"); 35 digester.addSetNext("Server/GlobalNamingResources", 36 "setGlobalNamingResources", 37 "org.apache.catalina.deploy.NamingResourcesImpl"); 38 39 digester.addRule("Server/Listener", 40 new ListenerCreateRule(null, "className")); 41 digester.addSetProperties("Server/Listener"); 42 digester.addSetNext("Server/Listener", 43 "addLifecycleListener", 44 "org.apache.catalina.LifecycleListener"); 45 46 digester.addObjectCreate("Server/Service", 47 "org.apache.catalina.core.StandardService", 48 "className"); 49 digester.addSetProperties("Server/Service"); 50 digester.addSetNext("Server/Service", 51 "addService", 52 "org.apache.catalina.Service"); 53 54 digester.addObjectCreate("Server/Service/Listener", 55 null, // MUST be specified in the element 56 "className"); 57 digester.addSetProperties("Server/Service/Listener"); 58 digester.addSetNext("Server/Service/Listener", 59 "addLifecycleListener", 60 "org.apache.catalina.LifecycleListener"); 61 62 //Executor 63 digester.addObjectCreate("Server/Service/Executor", 64 "org.apache.catalina.core.StandardThreadExecutor", 65 "className"); 66 digester.addSetProperties("Server/Service/Executor"); 67 68 digester.addSetNext("Server/Service/Executor", 69 "addExecutor", 70 "org.apache.catalina.Executor"); 71 72 73 digester.addRule("Server/Service/Connector", 74 new ConnectorCreateRule()); 75 digester.addRule("Server/Service/Connector", new SetAllPropertiesRule( 76 new String[]{"executor", "sslImplementationName", "protocol"})); 77 digester.addSetNext("Server/Service/Connector", 78 "addConnector", 79 "org.apache.catalina.connector.Connector"); 80 81 digester.addRule("Server/Service/Connector", new AddPortOffsetRule()); 82 83 digester.addObjectCreate("Server/Service/Connector/SSLHostConfig", 84 "org.apache.tomcat.util.net.SSLHostConfig"); 85 digester.addSetProperties("Server/Service/Connector/SSLHostConfig"); 86 digester.addSetNext("Server/Service/Connector/SSLHostConfig", 87 "addSslHostConfig", 88 "org.apache.tomcat.util.net.SSLHostConfig"); 89 90 digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate", 91 new CertificateCreateRule()); 92 digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate", 93 new SetAllPropertiesRule(new String[]{"type"})); 94 digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate", 95 "addCertificate", 96 "org.apache.tomcat.util.net.SSLHostConfigCertificate"); 97 98 digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf", 99 "org.apache.tomcat.util.net.openssl.OpenSSLConf"); 100 digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf"); 101 digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf", 102 "setOpenSslConf", 103 "org.apache.tomcat.util.net.openssl.OpenSSLConf"); 104 105 digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd", 106 "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd"); 107 digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd"); 108 digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd", 109 "addCmd", 110 "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd"); 111 112 digester.addObjectCreate("Server/Service/Connector/Listener", 113 null, // MUST be specified in the element 114 "className"); 115 digester.addSetProperties("Server/Service/Connector/Listener"); 116 digester.addSetNext("Server/Service/Connector/Listener", 117 "addLifecycleListener", 118 "org.apache.catalina.LifecycleListener"); 119 120 digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol", 121 null, // MUST be specified in the element 122 "className"); 123 digester.addSetProperties("Server/Service/Connector/UpgradeProtocol"); 124 digester.addSetNext("Server/Service/Connector/UpgradeProtocol", 125 "addUpgradeProtocol", 126 "org.apache.coyote.UpgradeProtocol"); 127 128 // Add RuleSets for nested elements 129 digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/")); 130 digester.addRuleSet(new EngineRuleSet("Server/Service/")); 131 digester.addRuleSet(new HostRuleSet("Server/Service/Engine/")); 132 digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/")); 133 addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/"); 134 digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/")); 135 136 // When the 'engine' is found, set the parentClassLoader. 137 digester.addRule("Server/Service/Engine", 138 new SetParentClassLoaderRule(parentClassLoader)); 139 addClusterRuleSet(digester, "Server/Service/Engine/Cluster/"); 140 141 long t2=System.currentTimeMillis(); 142 if (log.isDebugEnabled()) { 143 log.debug("Digester for server.xml created " + ( t2-t1 )); 144 } 145 return digester; 146 147 }
创建StanderService对象过程如下:调用StandardService实例的set方法设置属性,最后调用父节点Server的addService方法,将service添加到Server。
其他组件创建过程类似(但是Content组件和子组件有点特殊,后面再单独看)
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
2:调用standardServer的init方法完成完成Server组件的初始化
2.2:start方法:
getServer().start();
调用了server的start方法启动Tomcat的所有组件
3:Server类
server组件的init和start方法,最终调用的是Lifecycle的init和start方法。Lifecycle的实现类LifecycleBase的init方法(模板模式)
3.1:LifecycleBase的init方法:
1 public final synchronized void init() throws LifecycleException {
2 if (!state.equals(LifecycleState.NEW)) {
3 invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
4 }
5
6 try {
//1:状态变更事件
7 setStateInternal(LifecycleState.INITIALIZING, null, false);
// 2:server的初始化方法
8 initInternal();
9 setStateInternal(LifecycleState.INITIALIZED, null, false);
10 } catch (Throwable t) {
11 handleSubClassException(t, "lifecycleBase.initFail", toString());
12 }
13 }
1:状态变更事件(观察者模式)
2:调用StandardServer的initInternal方法,初始化各组件:后面子容器实现类都是Standard- 开头;状态变更事件稍后在看(如何注册事件,怎么通知?)
整个初始化链路如下图:
3.2:StandardServer的initInternal方法:
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize utility executor
reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
register(utilityExecutor, "type=UtilityExecutor");
onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared
// class loaders
if (getCatalina() != null) {
....
}
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
server的初始化工作,除了对自身做一些基础的初始化,主要是对service组件进行初始化(1个server可对应多个service)
3.3:StandardServer的startInternal方法:
startInternal方法主要是调用service的startInternal方法,子组件会做一些特殊的动作
4:Service类
4.1:initInternal方法:
protected void initInternal() throws LifecycleException {
super.initInternal();
//1:engine初始化
if (engine != null) {
engine.init();
}
// 2:线程池初始化
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// 3:Initialize mapper listener
mapperListener.init();
// 4:Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
connector.init();
}
}
}
service的初始化比较热闹,主要完成四件事,刚好对应到tomcat架构设计上service的功能
1)子组件的初始化
2)公共线程池
3)mapper listener请求映射
4)连接器
4.2:initInternal方法:
start方法和init方法也比较类似。
至此,顶层的公共逻辑已经完成,下面分为连接器、处理器、映射Mapper以及公共线程池独立的初始化和启动流程。
从上面的service的初始化开始进容器组件的初始化最顶层的Engine(Container作为容器组件的公共接口提供服务)。
5:Engine类
5.1:initInternal方法
protected void initInternal() throws LifecycleException {
getRealm();
super.initInternal();
}
从代码层面看,init方法调用了ContainerBase的init方法,启动一个线程池,那子容器的初始化在哪完成的呢?,继续看
5.2:startInternal方法
protected synchronized void startInternal() throws LifecycleException {
// Standard container startup
super.startInternal();
}
调用了ContainerBase的startInternal方法
protected synchronized void startInternal() throws LifecycleException {
// 1: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])));
}
MultiThrowable multiThrowable = null;
for (Future<Void> result : results) {
try {
result.get();
} catch (Throwable e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
if (multiThrowable == null) {
multiThrowable = new MultiThrowable();
}
multiThrowable.add(e);
}
}
if (multiThrowable != null) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
multiThrowable.getThrowable());
}
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
setState(LifecycleState.STARTING);
// Start our thread
if (backgroundProcessorDelay > 0) {
monitorFuture = Container.getService(ContainerBase.this).getServer()
.getUtilityExecutor().scheduleWithFixedDelay(
new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
}
}
上面代码核心在找到子组件并用线程池进行初始化(这里使用了init方法初始化的线程池)。
private static class StartChild implements Callable<Void> {
private Container child;
public StartChild(Container child) {
this.child = child;
}
@Override
public Void call() throws LifecycleException {
child.start();
return null;
}
上面线程的start方法调用了LifecycleBase的start方法:
5.3:LifecycleBase的start方法
public final synchronized void start() throws LifecycleException {
//1:状态事件是NEW,执行初始化
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);
//2:执行start方法
startInternal();
}
}
该方法完成有很多状态监听机制,其中就有对NEW状态执行init的方法,就是我们前面说的初始化Host组件,并调用了start方法进行启动过程
6:Host类
6.1:initInternal方法
用的还是父类ContainerBase的方法,也没有初始化Context容器,整个初始化的启动过程和Enginer类似
@Override
protected void initInternal() throws LifecycleException {
reconfigureStartStopExecutor(getStartStopThreads());
super.initInternal();
}
6.2:startInternal方法
protected synchronized void startInternal() throws LifecycleException {
// Set error report valve
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
try {
boolean found = false;
Valve[] valves = getPipeline().getValves();
for (Valve valve : valves) {
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
}
if(!found) {
Valve valve =
(Valve) Class.forName(errorValve).getConstructor().newInstance();
getPipeline().addValve(valve);
}
} catch (Throwable t) {
}
super.startInternal();
}
对Host的Pipeline添加了value,并调用了父类ContainerBase的startInternal方法(5.3),持续进行子组件的初始化工作。但是前面的server.xml的解析里面可以没有Context,那Context容器又如何初始化呢?