Tomcat源码解读系列(二)——Tomcat的核心组成和启动过程
聲明:源碼版本為Tomcat 6.0.35
前面的文章中介紹了Tomcat的基本配置,每個(gè)配置項(xiàng)也基本上對(duì)應(yīng)了Tomcat的組件結(jié)構(gòu),如果要用一張圖來(lái)形象展現(xiàn)一下Tomcat組成的話,整個(gè)Tomcat的組成可以如下圖所示:
Tomcat在接收到用戶請(qǐng)求時(shí),將會(huì)通過(guò)以上組件的協(xié)作來(lái)給最終用戶產(chǎn)生響應(yīng)。首先是最外層的Server和Service來(lái)提供整個(gè)運(yùn)行環(huán)境的基礎(chǔ)設(shè)施,而Connector通過(guò)指定的協(xié)議和接口來(lái)監(jiān)聽(tīng)用戶的請(qǐng)求,在對(duì)請(qǐng)求進(jìn)行必要的處理和解析后將請(qǐng)求的內(nèi)容傳遞給對(duì)應(yīng)的容器,經(jīng)過(guò)容器一層層的處理后,生成最終的響應(yīng)信息,返回給客戶端。
? ?????? Tomcat的容器通過(guò)實(shí)現(xiàn)一系列的接口,來(lái)統(tǒng)一處理一些生命周期相關(guān)的操作,而Engine、Host、Context等容器通過(guò)實(shí)現(xiàn)Container接口來(lái)完成處理請(qǐng)求時(shí)統(tǒng)一的模式,具體表現(xiàn)為該類容器內(nèi)部均有一個(gè)Pipeline結(jié)構(gòu),實(shí)際的業(yè)務(wù)處理都是通過(guò)在Pipeline上添加Valve來(lái)實(shí)現(xiàn),這樣就充分保證整個(gè)架構(gòu)的高度可擴(kuò)展性。Tomcat核心組件的類圖如下圖所示:
在介紹請(qǐng)求的處理過(guò)程時(shí),將會(huì)詳細(xì)介紹各個(gè)組件的作用和處理流程。本文將會(huì)主要分析Tomcat的啟動(dòng)流程,介紹涉及到什么組件以及初始化的過(guò)程,簡(jiǎn)單期間將會(huì)重點(diǎn)分析HTTP協(xié)議所對(duì)應(yīng)Connector啟動(dòng)過(guò)程。
Tomcat在啟動(dòng)時(shí)的重點(diǎn)功能如下:
- 初始化類加載器:主要初始化CommonLoader、CatalinaLoader以及SharedLoader;
- 解析配置文件:使用Digester組件解析Tomcat的server.xml,初始化各個(gè)組件(包含各個(gè)web應(yīng)用,解析對(duì)應(yīng)的web.xml進(jìn)行初始化);
- 初始化連接器:初始化聲明的Connector,以指定的協(xié)議打開(kāi)端口,等待請(qǐng)求。
不管是通過(guò)命令行啟動(dòng)還是通過(guò)Eclipse的WST server UI,Tomcat的啟動(dòng)流程是在org.apache.catalina.startup. Bootstrap類的main方法中開(kāi)始的,在啟動(dòng)時(shí),這個(gè)類的核心代碼如下所示:
public static void main(String args[]) {if (daemon == null) {daemon = new Bootstrap();//實(shí)例化該類的一個(gè)實(shí)例try {daemon.init();//進(jìn)行初始化} catch (Throwable t) {……;}}try {……//此處略去代碼若干行if (command.equals("start")) {daemon.setAwait(true);daemon.load(args);//執(zhí)行l(wèi)oad,生成組件實(shí)例并初始化daemon.start();//啟動(dòng)各個(gè)組件 }……//此處略去代碼若干行}從以上的代碼中,可以看到在Tomcat啟動(dòng)的時(shí)候,執(zhí)行了三個(gè)關(guān)鍵方法即init、load、和start。后面的兩個(gè)方法都是通過(guò)反射調(diào)用org.apache.catalina.startup.Catalina的同名方法完成的,所以后面在介紹時(shí)將會(huì)直接轉(zhuǎn)到Catalina的同名方法。首先分析一下Bootstrap的init方法,在該方法中將會(huì)初始化一些全局的系統(tǒng)屬性、初始化類加載器、通過(guò)反射得到Catalina實(shí)例,在這里我們重點(diǎn)看一下初始化類加載器的方法:
private void initClassLoaders() {try {commonLoader = createClassLoader("common", null);if( commonLoader == null ) {// no config file, default to this loader - we might be in a 'single' env.commonLoader=this.getClass().getClassLoader();}catalinaLoader = createClassLoader("server", commonLoader);sharedLoader = createClassLoader("shared", commonLoader);} catch (Throwable t) {log.error("Class loader creation threw exception", t);System.exit(1);}}在以上的代碼總,我們可以看到初始化了三個(gè)類加載器,這三個(gè)類加載器將會(huì)有篇博文進(jìn)行簡(jiǎn)單的介紹。
然后我們進(jìn)入Catalina的load方法:
public void load() { //……//初始化Digester組件,定義了解析規(guī)則Digester digester = createStartDigester();//……中間略去代碼若干,主要作用為將server.xml文件轉(zhuǎn)換為輸入流try {inputSource.setByteStream(inputStream);digester.push(this); //通過(guò)Digester解析這個(gè)文件,在此過(guò)程中會(huì)初始化各個(gè)組件實(shí)例及其依賴關(guān)系 digester.parse(inputSource);inputStream.close();} catch (Exception e) {}// 調(diào)用Server的initialize方法,初始化各個(gè)組件if (getServer() instanceof Lifecycle) {try {getServer().initialize();} 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);}}}在以上的代碼中,關(guān)鍵的任務(wù)有兩項(xiàng)即使用Digester組件按照給定的規(guī)則解析server.xml、調(diào)用Server的initialize方法。關(guān)于Digester組件的使用,后續(xù)會(huì)有一篇專門的博文進(jìn)行講解,而Server的initialize方法中,會(huì)發(fā)布事件并調(diào)用各個(gè)Service的initialize方法,從而級(jí)聯(lián)完成各個(gè)組件的初始化。每個(gè)組件的初始化都是比較有意思的,但是我們限于篇幅先關(guān)注Connector的初始化,這可能是最值得關(guān)注的。
Connector的initialize方法,核心代碼如下:
public void initialize() throws LifecycleException{//該適配器會(huì)完成請(qǐng)求的真正處理 adapter = new CoyoteAdapter(this);//對(duì)于不同的實(shí)現(xiàn),會(huì)有不同的ProtocolHandler實(shí)現(xiàn)類,我們來(lái)看 //Http11Protocol,它用來(lái)處理HTTP請(qǐng)求 protocolHandler.setAdapter(adapter);try {protocolHandler.init();} catch (Exception e) {……}}在Http11Protocol的init方法中,核心代碼如下:
public void init() throws Exception {endpoint.setName(getName());//endpoint為JIoEndpoint的實(shí)現(xiàn)類 endpoint.setHandler(cHandler);try {endpoint.init();//核心代碼就是調(diào)用 JIoEndpoint的初始化方法} catch (Exception ex) {……}}我們看到最終的初始化方法最終都會(huì)調(diào)到JIoEndpoint的init方法,網(wǎng)絡(luò)初始化和對(duì)請(qǐng)求的最初處理都是通過(guò)該類及其內(nèi)部類完成的,所以后續(xù)的內(nèi)容將會(huì)重點(diǎn)關(guān)注此類:
public void init() throws Exception {if (acceptorThreadCount == 0) {//接受請(qǐng)求的線程數(shù)acceptorThreadCount = 1;}if (serverSocket == null) {try {if (address == null) {//基于特定端口創(chuàng)建一個(gè)ServerSocket對(duì)象,準(zhǔn)備接受請(qǐng)求serverSocket = serverSocketFactory.createSocket(port, backlog);} else {serverSocket = serverSocketFactory.createSocket(port, backlog, address);}} catch (BindException orig) {……}}}在上面的代碼中,我們可以看到此時(shí)初始化了一個(gè)ServerSocket對(duì)象,用來(lái)準(zhǔn)備接受請(qǐng)求。
如果將其比作賽跑,此時(shí)已經(jīng)到了“各就各位”狀態(tài),就等最終的那聲“發(fā)令槍”了,而Catalina的start方法就是“發(fā)令槍”啦:
public void start() {if (getServer() == null) {load();}if (getServer() == null) {log.fatal("Cannot start server. Server instance is not configured.");return;}if (getServer() instanceof Lifecycle) {try {((Lifecycle) getServer()).start();} catch (LifecycleException e) {log.error("Catalina.start: ", e);}}//……}此時(shí)會(huì)調(diào)用Server的start方法,這里我們重點(diǎn)還是關(guān)注JIoEndpoint的start方法:
public void start() throws Exception {if (!initialized) {init();}if (!running) {running = true;paused = false;if (executor == null) {//初始化處理連接的線程,maxThread的默認(rèn)值為200,這也就是為什么 //說(shuō)Tomcat只能同時(shí)處理200個(gè)請(qǐng)求的來(lái)歷workers = new WorkerStack(maxThreads);}for (int i = 0; i < acceptorThreadCount; i++) {//初始化接受請(qǐng)求的線程Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);acceptorThread.setPriority(threadPriority);acceptorThread.setDaemon(daemon);acceptorThread.start();}}}從以上的代碼,可以看到,如果沒(méi)有在server.xml中聲明Executor的話,將會(huì)使用內(nèi)部的一個(gè)容量為200的線程池用來(lái)后續(xù)的請(qǐng)求處理。并且按照參數(shù)acceptorThreadCount的設(shè)置,初始化線程來(lái)接受請(qǐng)求。而Acceptor是真正的幕后英雄,接受請(qǐng)求并分派給處理過(guò)程:
protected class Acceptor implements Runnable {public void run() {while (running) {// 接受發(fā)送過(guò)來(lái)的請(qǐng)求Socket socket = serverSocketFactory.acceptSocket(serverSocket);serverSocketFactory.initSocket(socket);//處理這個(gè)請(qǐng)求if (!processSocket(socket)) {//關(guān)閉連接try {socket.close();} catch (IOException e) {// Ignore }}}}}從這里我們可以看到,Acceptor接受Socket請(qǐng)求,并調(diào)用processSocket方法來(lái)進(jìn)行請(qǐng)求的處理。至此,Tomcat的組件整裝待命,等待請(qǐng)求的到來(lái)。關(guān)于請(qǐng)求的處理,會(huì)在下篇文章中介紹。
轉(zhuǎn)載于:https://www.cnblogs.com/levinzhang/archive/2012/09/02/2667702.html
總結(jié)
以上是生活随笔為你收集整理的Tomcat源码解读系列(二)——Tomcat的核心组成和启动过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ecshop 奇偶行显示不同的商品样式
- 下一篇: 一个filter子查询测试