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,service,ThreadPool,Listener等
7 Digester digester = createStartDigester();
8 // 2:解析server.xml
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 {
// 3:呼叫server的init方法初始化Tomcat各元件
32 getServer().init();
33 } catch (LifecycleException e) {
34
35 }
36
37 }
核心呼叫了server的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方法,初始化各元件
先看第二步,呼叫了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方法,最終初始化了Host類的init方法(Container預設實現)和start方法
6:Host類
6.1: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方法,持續進行子元件的初始化工作。