結合源碼來探索啟動的流程
通過上一章的目錄結構和體系結構的研究,我們對tomcat有一個初步的認識
我們先找到入口函數。根據"startd"命令來推導
Bootstrap 下面的main方法就是入口函數
上面圖片只截取了啟動的時候兩個方法一個初始化,一個是開始啟動,在初始化之前的 init 方法是初始化一些環境參數。這里就沒有詳細說明。我們先看
if (command.equals("startd")) {
args[args.length - 1] = "start";
//先初始化一個必要的組件 如server connetor service
//這里的daemon 是就是 Bootstrap自己
daemon.load(args);
//開始加載Engine 下面相關東西,像Host Conetxt weapp
daemon.start();
}
我們再往下看Bootstrap 的 load函數
private void load(String[] arguments)
throws Exception {
// Call the load() method
String methodName = "load";
Object param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
// 反射獲取catalina 的對象的 load
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled())
log.debug("Calling startup class " + method);
//利用反射去調用 catalina的load方法
method.invoke(catalinaDaemon, param);
}
然后下一步我們去CatalinaDaemon驗證下是不是有一個無參的load方法。
果然有一個load方法(方法代碼太多只截取重要的部分)
public void load() {
//創建一個xml解析器
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
//讀取conf/server.xml文件
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", file), e);
}
}
····· //這里的一些代碼是保證讀到server.xml文件的內容
try {
inputSource.setByteStream(inputStream);
digester.push(this);
//解析 配置文件流,對配置文件里面的配置進行初始化
digester.parse(inputSource);
} catch (SAXParseException spe) {
log.warn("Catalina.start using " + getConfigFile() + ": " +
spe.getMessage());
return;
} catch (Exception e) {
log.warn("Catalina.start using " + getConfigFile() + ": " , e);
return;
} finally {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
·············· 下面就是開始 Server的初始化了
// Start the new server
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
我們跟到里面getServer().init();看看做了什么,進去之后發現到了"Lifecycle" 類的 init()方法 這是一個接口,這個接口有三個實現類
除了 LifecycleBase里面是有現實,其他兩個類都沒有實現,那就不用肯定是調用LifecycleBase的init方法了。
public final synchronized void init() throws LifecycleException {
//默認的實現
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
setStateInternal(LifecycleState.INITIALIZING, null, false);
try {
//爭對所有的子類初始化,這個地方調用的是自己的抽象方法。
initInternal();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
setStateInternal(LifecycleState.INITIALIZED, null, false);
}
點開 LifecycleBase類的 initInternal()方法,發現也是有很多實現,其實到這里我們應該去看哪個實現類了,肯定是跟Server有關的。于是乎我們就找到了
StandardServer 這個類的initInternal() 方法
protected void initInternal() throws LifecycleException {
//調用父類的 方法
super.initInternal();
// Register global String cache
// Note although the cache is global, if there are multiple Servers
// present in the JVM (may happen when embedding) then the same cache
// will be registered under multiple names
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
//結合之前的tomcat 體系結構圖中來看 globalNamingResources資源文件組件進行初始化
globalNamingResources.init();
··············· 此處代碼省略
//這個地方就驗證了官網說的 Server下面有多個Service 這的調
//service 里面的init方法
// Initialize our defined Services 初始化多個services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
這里Service 調用的init 方法是 Lifecycle的,
之前我們說Lifecycle init 是LifecycleBase實現了
我們看下LifecycleBase 的init 方法
public final synchronized void init() throws LifecycleException {
//默認的實現
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
setStateInternal(LifecycleState.INITIALIZING, null, false);
try {
//初始化 Service 組件
initInternal();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
setStateInternal(LifecycleState.INITIALIZED, null, false);
}
我們發現這個initInternal() 方法又是 LifecycleBase的,所以我們在找對應的實現類的時候,就可以參照之前方法,找跟service 相關的,如果只有一個StandardService類
protected void initInternal() throws LifecycleException{
······此處代碼省略
//這里是線程池的初始化,這個可以對照上面的圖來看
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
//線程池 初始化
executor.init();
}
// 初始化 mapper listener
mapperListener.init();
// Initialize our defined Connectors
//可以看到這里的connector 是多個的,用加鎖來保證線程安全
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
//connector就開始初始化了
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
這里調用每個 Connector的init方法,再一步步跟進去,發現還是之前的套路,
在Connector 的init 方法里面又會去調用 LifecycleBase 的 initInternal() 方法,這次我們還是去找Connector里面的實現
還是貼代碼
protected void initInternal() throws LifecycleException {
//其實到這里必要的組件在這個方法里面已經初始化完了
//bootstarp 方法 load 方法已經完成了
//這里還是老樣子調用父類LifecycleMBeanBase 去注冊bean
super.initInternal();
// Initialize adapter 這里就是初始化處理器
adapter = new CoyoteAdapter(this);
//綁定處理器 然后就是到下面 init 方法再去綁定
protocolHandler.setAdapter(adapter);
·········此處代碼省略
try {
//決定是用bio 還是nio nio2 apr 這些組件去處理請求
//我們看看這個里面做了些什么
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException
(sm.getString
("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
protocolHandler 這個類是一個接口,有兩個實現類,我們肯定是找
AbstractProtocol這個實現類 的init 方法,在這個方法里面有一段代碼也就是最后
try {
//在這里去調用,這里就是具體的綁定用 Apr 還是JIo 還是NIo
endpoint.init();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.initError",
getName()), ex);
throw ex;
}
那么到這里初始化的工作就全部做完了,看到這里肯定暈了。
Connector 、StandardService、StandardServer 他們都是Lifecycle的子類,其實到最后才發現整個 初始化過程都是圍繞著 Lifecycle init() 和 LifecycleBase initInternal() 方法來實現的。
其實這個初始化的過程,就是用 責任鏈的調用模式來實現的, 一個鏈接著一個鏈來的任務來,最終完成所有的初始化。
華麗的分割線
初始化完成了,那我們再去看看啟動,回到 Bootstrap 的main 方法
不說廢話直接上圖
if (command.equals("startd")) {
args[args.length - 1] = "start";
//先初始化一個必要的組件 如server connetor service
//這里的daemon 是就是 Bootstrap自己
daemon.load(args);
//開始加載Engine 下面相關東西,像Host Conetxt weapp
daemon.start();
}
public void start()
throws Exception {
//還是跟之前是一樣的方法 反射調用catalina里面的start 方法
if( catalinaDaemon==null ) init();
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);
}
我們再看 catalina 里面的start 方法
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();
//前面都是做一些判空的操作,下面server 開始啟動
// Start the new server
try {
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;
}
}
我們按照之前的思維來分析 這個start 方法肯定又是Lifecycle 接口的一個方法,我們再去找相關的實現類 LifecycleBase,在代碼中又調用了自己的startInternal方法,所以我們還是找相關的實現類StandardServer
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
//加載一些資源文件
globalNamingResources.start();
// Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
// 跟之前的方式一樣 ,開始每一個service啟動
services[i].start();
}
}
}
我們跟到StandardService 的 startInternal方法
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// Start our defined Container first
if (container != null) {
synchronized (container) {
//這個地方就是 啟動Engine、Host、context 這些組件
container.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
//啟動線程池
executor.start();
}
}
//以及一些監聽器
mapperListener.start();
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
//啟動container 連接器管理
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}
我們可以看到 connector 和 executor 的組件啟動。
這里我們繼續 Connector 的 startInternal() 方法
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPort() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING);
try {
//這里就跟處理請求的,模式 是Bio NIO ···等方式, 這個在之前初始化的時候就已經選擇好了
protocolHandler.start();
} catch (Exception e) {
String errPrefix = "";
if(this.service != null) {
errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
}
throw new LifecycleException
(errPrefix + " " + sm.getString
("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
上面代碼中 我們先看下 container.start() 這里,點進去發現這里是調用的ContainerBase 這個類startInternal()方法
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
getLogger();
Cluster cluster = getClusterInternal();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
Realm realm = getRealmInternal();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
// Start our child containers, if any
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
//通過責任鏈的 調用 不同的調用子類的start
//這里的子類 有 Engine、Host、context 這些
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();
}
關注 new StartChild(children[i] )這段代碼
這里會調用 engine 子容器的 start 方法。也就是Host,Host 會將一個一個的項目編譯后的class 文件加進來
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).newInstance();
getPipeline().addValve(valve);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString(
"standardHost.invalidErrorReportValveClass",
errorValve), t);
}
}
super.startInternal();
}
最后又去調用父類的startInternal(),然后 調用 父類里面的threadStart() 方法
protected void threadStart() {
if (thread != null)
return;
if (backgroundProcessorDelay <= 0)
return;
threadDone = false;
String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
//每個子類的run方法 任務
thread = new Thread(new ContainerBackgroundProcessor(), threadName);
thread.setDaemon(true);
thread.start();
}
ContainerBackgroundProcessor 是實現了 Runnable d 線程類
processChildren 方法 里面去處理 各個子類的線程
container.backgroundProcess(); 這個是各個子類的事件的觸發器
這個方法最后 fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null)
這一段是調用LifecycleSupport 類
public void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
LifecycleListener interested[] = listeners;
for (int i = 0; i < interested.length; i++)
interested[i].lifecycleEvent(event);
}
然后又會用調用 LifecycleListener lifecycleEvent這個接口的方法
這個接口里面我們看到有很多實現
其實可以看出很多子類的具體任務都是在這里處理的。
我們找一下實現 HostConfig 里面的實現 lifecycleEvent方法
的check()方法
protected void check() {
if (host.getAutoDeploy()) {
// Check for resources modification to trigger redeployment
DeployedApplication[] apps =
deployed.values().toArray(new DeployedApplication[0]);
for (int i = 0; i < apps.length; i++) {
if (!isServiced(apps[i].name))
checkResources(apps[i]);
}
// Check for old versions of applications that can now be undeployed
if (host.getUndeployOldVersions()) {
checkUndeploy();
}
// Hotdeploy applications
//開始部署應用
deployApps();
}
}
這個地方就是開始部署應用了。
protected void deployApps() {
//部署web 項目
File appBase = host.getAppBaseFile();
//找到config的配置文件路徑
File configBase = host.getConfigBaseFile();
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs
//這個filteredAppPaths 是返回這個路徑下面 所有的文件夾及文件名
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
到這個地方 整個tomcat 的啟動流程就完了。