tomcat8 原始碼分析 | 元件及啟動過程
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();使用這種方式依次啟動子容器