tomcat 8 源碼分析 ,本文主要講解tomcat擁有哪些組件,容器,又是如何啟動的
推薦訪問我的個人網站,排版更好看呦: https://chenmingyu.top/tomcat-source-code/
tomcat
簡介
Tomcat是Apache 軟件基金會(Apache Software Foundation)的Jakarta 項目中的一個核心項目,Tomcat服務器是一個免費的開放源代碼的Web 應用服務器,屬於輕量級應用服務器。
整體架構
首先我們先看一張圖
看上圖總結一下tomcat的組件主要包括:
-
server:整個servlet容器,一個tomcat對應一個server,一個server包含多個service
server在tomcat中的實現類是:StandardServer
-
service: 一個service包含多個connector(接受請求的協議),和一個container(容器)
多個connector共享一個container容器,
service在tomcat中的實現類是:StandardService
-
connector:鏈接器,負責處理客戶端請求,解析不同協議及io方式
-
executor:線程池
-
container:包含engine,host,context,wrapper等組件
-
engine:servlet引擎,container容器中頂層的容器對象,一個engine可以包含多個host主機
engine在tomcat中的實現類是:StandardEngine
-
host:engine容器的子容器,一個host對應一個網絡域名,一個host包含多個context
host在tomcat中的實現類是:StandardHost
-
context:host容器的子容器,表示一個web應用
context在tomcat中的實現類是:StandardContext
-
wrapper:tomcat中最小的容器單元,表示web應用中的servlet
wrapper在tomcat中的實現類是:StandardWrapper
所以tomcat的組件結構大概是這個樣子的:
生命周期:Lifecycle
tomcat的啟動過程非常規范,使用Lifecycle接口統一管理各組件的生命周期,根據各個組件之間的父子級關系,首先調用init()方法逐級初始化各組件,然后在調用start()的方法進行啟動;
Lifecycle接口提供的方法如下,提供了init,start,destory等方法:
tomcat中的組件基本都繼承了LifecycleMBeanBase類,LifecycleMBeanBase集成LifecycleBase,LifecycleBase實現Lifecycle接口:
LifecycleBase重寫Lifecycle接口,比如init()方法,在init()方法中調用initInternal()方法,initInternal()方法是抽象方法,具體實現交由各個子類(組件)去實現。如果沒有實現initInternal()方法,則調用默認的LifecycleMBeanBase的initInternal方法。
啟動過程
接下來從源碼看一下tomcat的啟動流程:
bootstrap
tomcat的入口類為BootStrap的main方法
Bootstrap中main()方法如下,不重要的代碼省略了
/**
* Main method and entry point when starting Tomcat via the provided
* scripts.
*
* @param args Command line arguments to be processed
*/
public static void main(String args[]) {
.....
//初始化
bootstrap.init();
.....
if (command.equals("startd")) {
args[args.length - 1] = "start";
//實例化各組件 調用Catalina類的load方法
daemon.load(args);
//啟動各組件 調用Catalina類的start方法
daemon.start();
}
.....
}
bootstrap.init()的工作是初始化Bootstrap類,包含初始化類加載器
/**
* Initialize daemon.
* @throws Exception Fatal initialization error
*/
public void init() throws Exception {
//初始化類加載
initClassLoaders();
......
//實例化Catalina類
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
......
catalinaDaemon = startupInstance;
}
Catalina
接着調用剛初始化的Catalina類的實例catalinaDaemon的load()方法,重要的就兩點
/**
* Start a new server instance.
*/
public void load() {
.....
// Digester... 實例化組件
Digester digester = createStartDigester();
.....加載server.xml......
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
......
// 初始化sever
getServer().init();
}
Digester
Digester是一種將xml轉化為java對象的事件驅動型工具,通過讀取xml文件,當識別到特定的節點的時候會執行特定的動作,創建java對象或者執行對象的某個方法
通過Digester去創建了Catania中的大量初始化工作,具體詳見源碼:
// 創建server實例
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
//創建Executor
digester.addObjectCreate("Server/Service/Executor",
"org.apache.catalina.core.StandardThreadExecutor",
"className");
...等等大量初始化工作...
接着講,getServer().init()方法的作用是初始化Sever,調用LifecycleBase的init()方法,在init方法中調用的是StandardServer類initInternal()方法
StandardServer
StandardServer類圖如下:
StandardServer類initInternal()方法:
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
...省略很多,但是主要的在下面...
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
//調用services的init
services[i].init();
}
}
前面的時候講過一個server初始化多個services;
StandardService
services[i].init();初始化的是StandardService類,類圖如下
StandardService的initInternal() 方法的工作是初始化engine組件,初始化線程池,初始化mapperListener,初始化connector
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
//初始化engine
engine.init();
//初始化線程池
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
//初始化mapperListener
// Initialize mapper listener
mapperListener.init();
//初始化connector
connector.init();
}
初始化executor,mapperListener,connector后面再講其作用,先接初始化engine
StandardEngine
StandardEngine的類圖如下:
在StandardEngine的初始化中並沒有直接調用host的初始化,而是調用的父類containerBase的initInternal的方法:
//StandardEngine
@Override
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}
//containerBase
@Override
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}
host的init是在start階段去做的,所以后面再說
executor
executor.init();默認調用LifecycleMBeanBase的initInternal方法
mapperListener
mapperListener.init();也默認調用LifecycleMBeanBase的initInternal方法
connector
connector的初始化調用Connector類的initInternal方法,主要是new了一個CoyoteAdapter,初始化protocolHandler
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// 實例化 CoyoteAdapter 適配器
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
......
try {
//初始化 protocolHandler
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
}
ProtocolHandler.init();的實現:
AbstractProtocol是調用endpoint的init方法,這個方法中調用bind()
@Override
public void init() throws Exception {
//初始化endpoint
endpoint.init();
}
bind()針對不同的io類型提供了三種的默認實現
進入NioEndpoint類的bind()
/**
* Initialize the endpoint.
*/
@Override
public void bind() throws Exception {
//使用nio方式監聽端口
if (!getUseInheritedChannel()) {
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.socket().bind(addr,getAcceptCount());
}
//設置非阻塞
serverSock.configureBlocking(true); //mimic APR behavior
......
//開啟selectorPool
selectorPool.open();
}
start過程
tomcat的start階段與init階段相似,都是逐層調用,稍有不同的是在於engine,host,context,wrapper的啟動方式;
首先回到Bootstrap的main方法中,繼續執行Catalina類的start(),在start()方法中調getServer().start();
調用LifecycleBase類的start()方法,在這個方法中調動StandardServer類實現的startInternal(),在這個類中繼續調用service的star()方法,以此類推逐層start調用,直到調用engine的start(),我們看下engine的start()方法,在看下StandardEngine的類圖:
StandardEngine的startInternal()調用ContainerBase的startInternal()
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
......
// 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])));
}
......
}
findChildren()的方法找到的是engine容器的子容器然后在new StartChild(children[i])中調用子類容器的start();使用這種方式依次啟動子容器