上一節(jié)我們引出了calatina類進(jìn)行應(yīng)用加載,再回顧下調(diào)用代碼
daemon.setAwait(true);
daemon.load(args);
daemon.start();
對應(yīng)在calatina中都能找到相應(yīng)方法。setAwait(true)表示阻塞等待的含義我們先跳過。此類中有兩個重點要分析的方法load()和start(),一個加載配置,一個啟動應(yīng)用,看下我分析完的整個思維導(dǎo)圖,圖片太大,可以查看原圖

我將轉(zhuǎn)化為我們重點分析類的時序圖:

放大時序圖,我們看到了如下流程
-
catalina 先調(diào)用load加載了server.xml,配置文件生成了對應(yīng)tocmat架構(gòu)的主要類圖,此時通過Digester技術(shù)實現(xiàn),主要類圖如下
tomcat主要類圖.jpg
其中stndardService為每一個service服務(wù),connector為此service中的一個接收器,它和standardEngine代表的應(yīng)用容器通過MapperListener進(jìn)行映射,standardEngine內(nèi)部又分成standardHost、standardConetxt還有StandardWrapper(未化出,后生成),HostConfig和ConetxtConfig為對應(yīng)host和context的事件監(jiān)聽者用于初始化web應(yīng)用類。
catalina方法的start方法,最終調(diào)用到standardService的start()方法,此方法采用模版方法先調(diào)用父級的公用模版方法,最后調(diào)用自己的startInernal方法,最終將會初始化整個service服務(wù)。
standardService的啟動方法中核心代碼如下:
protected void startInternal() throws LifecycleException {
.....
//更新tomcat狀態(tài)
setState(LifecycleState.STARTING);
// 啟動容器服務(wù)
if (container != null) {
synchronized (container) {
container.start();
}
}
//開啟定義線程
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
// 開啟接收connector
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);
}
}
}
}
核心部分是三個服務(wù)啟動,我們重點看container.start()和connector.start()方法。
- container.start()方法啟動業(yè)務(wù)容器,其都遵守tomcat的生命周期最高接口Lifecycle來管理,通過事件驅(qū)動模式,配置觀察者模式,通過觀察tomcat的狀態(tài)變化,進(jìn)行啟動驅(qū)動。
此時有個重點類為ContainBase,此類是StandardEngine、StandardHost、StandardPipeline、StandardWrapper抽象父類,看下ContainBase中的重點方法
第一,startInternal方法,每層容器,查找子容器,開啟子容器,添加pipeline通道,更新此容器狀態(tài),驅(qū)動監(jiān)聽者進(jìn)行狀態(tài)更新,相關(guān)操作。
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
...........
//添加子容器,啟動子容器
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
children[i].start();
}
// 啟動當(dāng)前容器的pipeline,配置調(diào)用責(zé)任鏈
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
//事件驅(qū)動,通知當(dāng)前容器的監(jiān)聽者,進(jìn)行相應(yīng)操作
setState(LifecycleState.STARTING);
// Start our thread
threadStart();
}
第二,setState方法,事件驅(qū)動方法
private synchronized void setStateInternal(LifecycleState state,
Object data, boolean check) throws LifecycleException {
。。。。。。。。
this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
//事件驅(qū)動
fireLifecycleEvent(lifecycleEvent, data);
}
}
此方法繼續(xù)進(jìn)入,會看到
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);
}
第三,類圖已經(jīng)說了兩個重點的監(jiān)聽者HostConfig和ContextConfig,HostConfig用于找出目錄下的war包
protected void deployApps(String name) {
//根據(jù)你配置的appBase路徑,查找文件
File appBase = appBase();
//判斷你是否在conf/Catalina/你應(yīng)用名.xml中定制自己的應(yīng)用文件,沒有定制,后面將會使用默認(rèn)的context.xml
File configBase = configBase();
ContextName cn = new ContextName(name);
String baseName = cn.getBaseName();
// Deploy XML descriptors from configBase
File xml = new File(configBase, baseName + ".xml");
if (xml.exists())
deployDescriptor(cn, xml, baseName + ".xml");
// Deploy WARs, and loop if additional descriptors are found
File war = new File(appBase, baseName + ".war");
if (war.exists())
deployWAR(cn, war, baseName + ".war");
// Deploy expanded folders
File dir = new File(appBase, baseName);
if (dir.exists())
deployDirectory(cn, dir, baseName);
}
再看下 deployWAR()方法發(fā)布我們常見的war包,其核心邏輯
protected void deployWAR(ContextName cn, File war, String file) {
............
//生成StandardContext類
context = (Context) Class.forName(contextClass).newInstance();
.........
//配置ContextConfig監(jiān)聽器
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
context.addLifecycleListener(listener);
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(file);
//host中添加context,并將在此方法中啟動conetxt
host.addChild(context);
..........
deployed.put(cn.getName(), deployedApp);
}
第四,addChildInternal()增加子容器方法,上一步host.addChild(context)添加context,隨后addChildInternal中啟動了context,然后還是驅(qū)動模式通過fireContainerEvent方法通知觀察者。
private void addChildInternal(Container child) {
...............
//啟動子容器
child.start();
success = true;
............
//驅(qū)動事件發(fā)生
fireContainerEvent(ADD_CHILD_EVENT, child);
}
- ContextConfig類,查看其接收事件方法,最后進(jìn)行驅(qū)動configureStart方法發(fā)布服務(wù)
public void lifecycleEvent(LifecycleEvent event) {
// Identify the context we are associated with
try {
context = (Context) event.getLifecycle();
} catch (ClassCastException e) {
log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
return;
}
// 啟動服務(wù)
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
configureStart();
.....
}
查看configureStart方法
*/
protected synchronized void configureStart() {
...............
//創(chuàng)建WebXm解析其
createWebXmlDigester(context.getXmlNamespaceAware(),
context.getXmlValidation());
//解析web.xml
webConfig();
.............
}
最后終于看到熟悉的web.xml,看下 webConfig()方法,此時解析web.xml,解析所有servlet,并通過webXml.configureContext(context)生成對應(yīng)的每個StandardWrapper,
protected void webConfig() {
WebXml webXml = createWebXml();
// Parse global web.xml if present
InputSource globalWebXml = getGlobalWebXmlSource();
if (globalWebXml == null) {
// This is unusual enough to log
log.info(sm.getString("contextConfig.defaultMissing"));
} else {
parseWebXml(globalWebXml, webXml, false);
}
// Parse host level web.xml if present
// Additive apart from welcome pages
webXml.setReplaceWelcomeFiles(true);
InputSource hostWebXml = getHostWebXmlSource();
parseWebXml(hostWebXml, webXml, false);
// Parse context level web.xml
webXml.setReplaceWelcomeFiles(true);
InputSource contextWebXml = getContextWebXmlSource();
parseWebXml(contextWebXml, webXml, false);
// Assuming 0 is safe for what is required in this case
double webXmlVersion = 0;
if (webXml.getVersion() != null) {
webXmlVersion = Double.parseDouble(webXml.getVersion());
}
if (webXmlVersion >= 3) {
// Ordering is important here
// Step 1. Identify all the JARs packaged with the application
// If the JARs have a web-fragment.xml it will be parsed at this
// point.
Map<String,WebXml> fragments = processJarsForWebFragments();
// Only need to process fragments and annotations if metadata is
// not complete
Set<WebXml> orderedFragments = null;
if (!webXml.isMetadataComplete()) {
// Step 2. Order the fragments.
orderedFragments = WebXml.orderWebFragments(webXml, fragments);
// Step 3. Look for ServletContainerInitializer implementations
if (ok) {
processServletContainerInitializers(orderedFragments);
}
// Step 4. Process /WEB-INF/classes for annotations
// This will add any matching classes to the typeInitializerMap
if (ok) {
URL webinfClasses;
try {
webinfClasses = context.getServletContext().getResource(
"/WEB-INF/classes");
processAnnotationsUrl(webinfClasses, webXml);
} catch (MalformedURLException e) {
log.error(sm.getString(
"contextConfig.webinfClassesUrl"), e);
}
}
// Step 5. Process JARs for annotations - only need to process
// those fragments we are going to use
// This will add any matching classes to the typeInitializerMap
if (ok) {
processAnnotations(orderedFragments);
}
// Step 6. Merge web-fragment.xml files into the main web.xml
// file.
if (ok) {
ok = webXml.merge(orderedFragments);
}
// Step 6.5 Convert explicitly mentioned jsps to servlets
if (!false) {
convertJsps(webXml);
}
// Step 7. Apply merged web.xml to Context
if (ok) {
webXml.configureContext(context);
// Step 7a. Make the merged web.xml available to other
// components, specifically Jasper, to save those components
// from having to re-generate it.
// TODO Use a ServletContainerInitializer for Jasper
String mergedWebXml = webXml.toXml();
context.getServletContext().setAttribute(
org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,
mergedWebXml);
if (context.getLogEffectiveWebXml()) {
log.info("web.xml:\n" + mergedWebXml);
}
}
} else {
webXml.configureContext(context);
}
// Always need to look for static resources
// Step 8. Look for static resources packaged in JARs
if (ok) {
// Spec does not define an order.
// Use ordered JARs followed by remaining JARs
Set<WebXml> resourceJars = new LinkedHashSet<WebXml>();
if (orderedFragments != null) {
for (WebXml fragment : orderedFragments) {
resourceJars.add(fragment);
}
}
for (WebXml fragment : fragments.values()) {
if (!resourceJars.contains(fragment)) {
resourceJars.add(fragment);
}
}
processResourceJARs(resourceJars);
// See also StandardContext.resourcesStart() for
// WEB-INF/classes/META-INF/resources configuration
}
// Only look for ServletContainerInitializer if metadata is not
// complete
if (!webXml.isMetadataComplete()) {
// Step 9. Apply the ServletContainerInitializer config to the
// context
if (ok) {
for (Map.Entry<ServletContainerInitializer,
Set<Class<?>>> entry :
initializerClassMap.entrySet()) {
if (entry.getValue().isEmpty()) {
context.addServletContainerInitializer(
entry.getKey(), null);
} else {
context.addServletContainerInitializer(
entry.getKey(), entry.getValue());
}
}
}
}
} else {
// Apply unmerged web.xml to Context
convertJsps(webXml);
webXml.configureContext(context);
}
}
最終的StandardWrapper類中存了每個servlet的相關(guān)信息。
到這里,業(yè)務(wù)容器從Engine-->Host--->Conetext--->Wrapper層層驅(qū)動,初始化了整個web服務(wù)處理核心。
- 業(yè)務(wù)容器啟動完后,啟動Connector,根據(jù)LifecycleBase模版模式,最終落到startInternal方法上
protected void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
try {
//啟動通信處理handler,開啟endpoint,監(jiān)聽socket端口
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);
}
//綁定connect和container關(guān)系,最后存入其屬性`Mapper mapper`中
mapperListener.start();
}
再看下endpoint開啟監(jiān)聽的代碼,其中設(shè)置了接收線程等配置,并異步開啟了Acceptor進(jìn)行端口監(jiān)聽。
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
// Create worker collection
if ( getExecutor() == null ) {
createExecutor();
}
initializeConnectionLatch();
// 控制輪詢線程
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i<pollers.length; i++) {
pollers[i] = new Poller();
Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
// 控制接收線程
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(getDaemon());
acceptorThread.start();
}
}
}
看下Acceptor類,socket監(jiān)聽實現(xiàn):
protected class Acceptor implements Runnable {
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused && running) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
if (!running) {
break;
}
try {
//if we have reached max connections, wait
awaitConnection();
SocketChannel socket = null;
try {
// Accept the next incoming connection from the server
// socket
socket = serverSock.accept();
} catch (IOException ioe) {
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
}
// Successful accept, reset the error delay
errorDelay = 0;
// Hand this socket off to an appropriate processor
//TODO FIXME - this is currently a blocking call, meaning we will be blocking
//further accepts until there is a thread available.
if ( running && (!paused) && socket != null ) {
// setSocketOptions() will add channel to the poller
// if successful
if (!setSocketOptions(socket)) {
try {
socket.socket().close();
socket.close();
} catch (IOException ix) {
if (log.isDebugEnabled())
log.debug("", ix);
}
} else {
countUpConnection();
}
}
} catch (SocketTimeoutException sx) {
//normal condition
} catch (IOException x) {
if (running) {
log.error(sm.getString("endpoint.accept.fail"), x);
}
} catch (OutOfMemoryError oom) {
try {
oomParachuteData = null;
releaseCaches();
log.error("", oom);
}catch ( Throwable oomt ) {
try {
try {
System.err.println(oomParachuteMsg);
oomt.printStackTrace();
}catch (Throwable letsHopeWeDontGetHere){
ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
}
}catch (Throwable letsHopeWeDontGetHere){
ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
}
}//while
}//run
}
到此,web服務(wù)全部啟動成功
總結(jié)下知識點:
- Digester 解析xml技術(shù)
- LifecycleBase生命周期管理
- fireContainerEvent事件驅(qū)動模式
- ExceptionUtils.handleThrowable(Throwable t)異常統(tǒng)一處理方式
目錄:?tomcat 源碼學(xué)習(xí)系列
上一篇: ? tomcat啟動源碼分析(一)--入口代碼Bootstrap初始化
下一篇:? tomcat啟動源碼分析(三)--http請求nio處理
