爪哇東南的自留地

          學(xué)習(xí)探討開源和web開發(fā)

          導(dǎo)航

          <2006年9月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          1234567

          統(tǒng)計(jì)

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          相冊(cè)

          收藏夾

          life

          technique

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          tomcat 4.1.30啟動(dòng)過(guò)程的源碼分析

          前幾天為了解決sinpool兄的《多線程的問題。》一帖,專門看了一下tomcat 4.1.30的源碼,
          其中重點(diǎn)研究了tomcat的啟動(dòng)這一部分,個(gè)人感覺tomcat的源碼還是寫的很清楚易懂,值得一看。
          (以前看過(guò)struts的部分代碼,感覺也比較經(jīng)典)????
          然后我看后的代碼整理了一下,附在下面,希望對(duì)其他人有用,也希望感興趣的兄弟可以多看看好的代碼,
          肯定對(duì)自己的程序設(shè)計(jì)和代碼質(zhì)量頗有益處。

          一. 啟動(dòng)類(包含main()方法的類):
          org.apache.catalina.startup.Bootstrap
          這個(gè)類是tomcat的啟動(dòng)類,主要按照如下步驟,進(jìn)行主要的啟動(dòng)工作:
          1. 創(chuàng)建3個(gè)ClassLoader:common,catalina和share,它們對(duì)應(yīng)tomcat的3個(gè)Classloader,我想對(duì)tomcat
          的classloader有研究的兄弟對(duì)這個(gè)肯定不陌生,其中common classloader是緊跟在系統(tǒng)的classloader(也就是
          系統(tǒng)環(huán)境變量中設(shè)置的CLASSPATH所對(duì)應(yīng)的classloader),而catalina classloader是common的子classloader,是tomcat
          運(yùn)行所需要的類的classloader,而shared classloader也是common的子classloader,是和catalina平級(jí)的classloader,
          之所以說(shuō)是shared classloader,是因?yàn)樗撬衪omcat下面發(fā)布的webapp classloader(每一個(gè)web app都有一個(gè)自己的classloader)
          的父classloader。它們這3個(gè)classloader分別讀取tomcat home下面的common, server和shared三個(gè)目錄里面的classes和lib目錄,
          用于初始化自己所控制的類庫(kù)和資源。

          2. 創(chuàng)建啟動(dòng)一個(gè)的org.apache.catalina.startup.Catalina類的實(shí)例,并調(diào)用它的process方法,這里使用的是java的reflection技術(shù)。
          然后調(diào)用這個(gè)實(shí)例的process方法,并把Bootstrap接受到的命令行參數(shù)傳遞進(jìn)去了,這里Bootstrap類并沒有解析傳給它的命令行參數(shù)。
          當(dāng)然在調(diào)用process之前還使用setParentClassLoader方法設(shè)置了一下父classloader。這里簡(jiǎn)單介紹一下有關(guān)classloader的一個(gè)重要
          特性,就是如果classloader要load一個(gè)類時(shí),不是自己先找,而是先把這個(gè)任務(wù)委派給自己的父classloader,然后自己的父classloader
          也不找,在把這個(gè)任務(wù)委派給自己的父classloader,直到找到最頂層的classloader,然后再自頂向下的找對(duì)應(yīng)的這個(gè)要load的類的定義,
          如果那個(gè)classloader先找到,就返回。所以接合上面第一點(diǎn)介紹的tomcat中3個(gè)classloader,大家就可以明白tomca的classloader找類
          的順序了,這個(gè)對(duì)程序開發(fā)人員來(lái)說(shuō)特別重要。我想使用過(guò)tomcat或者其他app server的兄弟肯定碰到過(guò)一個(gè)類明明存在可就是找不到,
          或者總是找到一個(gè)老的版本,我想主要是在多個(gè)地方放置的原因,或者哪里有重名的類:-)

          二.org.apache.catalina.startup.Catalina類
          現(xiàn)在程序轉(zhuǎn)到org.apache.catalina.startup.Catalina類里面的process方法。
          這個(gè)方法首先設(shè)置一下catalina的home和base目錄,然后通過(guò)arguments方法解析命令行參數(shù),
          最后調(diào)用execute()方法啟動(dòng)server。而execute方法很簡(jiǎn)單,就是根據(jù)arguments解析的命令行參數(shù),
          決定是啟動(dòng)server,還是stop server,如果是start server,就調(diào)用start方法,而下面重點(diǎn)講一下這個(gè)start()方法,
          因?yàn)椴潘闶且粋€(gè)真正開始的啟動(dòng)tomcat的地方:-)
          1. start方法首先使用Digester(這個(gè)東東是jakarta commons里面的一個(gè)用于解析xml文件的工具包,一開始是專門用于解析struts配置文件的,
          后來(lái)被抽象成現(xiàn)在的一個(gè)通用工具,主要還是用來(lái)解析xml配置文件,根據(jù)一些定義的rule自動(dòng)生成對(duì)應(yīng)的類的實(shí)例,具體信息可以參考
          apache網(wǎng)站上的文檔)來(lái)設(shè)置tomcat配置文件,也就是/conf/server.xml這個(gè)文件的解析規(guī)則
          然后通過(guò)如下代碼來(lái)將配置文件中的數(shù)據(jù)轉(zhuǎn)化成內(nèi)存中的實(shí)例:

          代碼:
          ??????? File file = configFile();

          ??????? try {

          ??????????? InputSource is =

          ??????????????? new InputSource("file://" + file.getAbsolutePath());

          ??????????? FileInputStream fis = new FileInputStream(file);

          ??????????? is.setByteStream(fis);

          ??????????? digester.push(this);

          ??????????? digester.parse(is);

          ??????????? fis.close();

          ??????? } catch (Exception e) {

          ??????????? System.out.println("Catalina.start: " + e);

          ??????????? e.printStackTrace(System.out);

          ??????????? System.exit(1);

          ??????? }???
          ????
          ?


          轉(zhuǎn)換的規(guī)則如下(我只作一些簡(jiǎn)單的介紹),例如配置文件中的
          a. Server對(duì)應(yīng)可以產(chǎn)成一個(gè)org.apache.catalina.core.StandardServer類(這個(gè)類很重要,是tomcat server的實(shí)現(xiàn))
          b. Server/GlobalNamingResources對(duì)應(yīng)生成org.apache.catalina.deploy.NamingResources類
          而大家比較熟悉的監(jiān)聽8080端口的類配置如下:
          c. Server/Service/Connector:org.apache.catalina.connector.http.HttpConnector
          d. Server/Service/Engine/Host/Context/:org.apache.catalina.core.StandardContext
          有興趣的兄弟可以參考jakarta commons里面的Digester文檔和org.apache.catalina.startup.Catalina
          這個(gè)類里面的createStartDigester方法.
          在這段代碼之后,一個(gè)叫server的變量已經(jīng)通過(guò)Digester工具生成了,它將會(huì)用于啟動(dòng)tomcat。
          2. 然后程序進(jìn)行了一些server啟動(dòng)前的設(shè)置工作,例如重定向log輸出流等等。而server啟動(dòng)的代碼如下:

          代碼:
          ??????? // Start the new server

          ??????? if (server instanceof Lifecycle) {

          ??????????? try {

          ??????????????? server.initialize();

          ??????????????? ((Lifecycle) server).start();

          ??????????????? try {

          ??????????????????? // Register shutdown hook

          ??????????????????? Runtime.getRuntime().addShutdownHook(shutdownHook);

          ??????????????? } catch (Throwable t) {

          ??????????????????? // This will fail on JDK 1.2. Ignoring, as Tomcat can run

          ??????????????????? // fine without the shutdown hook.

          ??????????????? }

          ??????????????? // Wait for the server to be told to shut down

          ??????????????? server.await();

          ??????????? } catch (LifecycleException e) {

          ??????????????? System.out.println("Catalina.start: " + e);

          ??????????????? e.printStackTrace(System.out);

          ??????????????? if (e.getThrowable() != null) {

          ??????????????????? System.out.println("----- Root Cause -----");

          ??????????????????? e.getThrowable().printStackTrace(System.out);

          ??????????????? }

          ??????????? }

          ??????? }

          ????
          ?


          其中server這個(gè)變量就是在剛才Digester解析時(shí)創(chuàng)建好的。
          (當(dāng)時(shí)這個(gè)地方我看了很長(zhǎng)時(shí)間,后來(lái)才發(fā)現(xiàn)是這樣的,因?yàn)橐郧安惶私釪igester這個(gè)東東)。
          然后大家可以看到server啟動(dòng)主要是分3步:
          1. initialize方法進(jìn)行server啟動(dòng)的初始化操作
          2. start方法啟動(dòng)server,主要是server中的的service和service中的connector
          3. await方法等待server shutdown
          其中我重點(diǎn)給大家介紹一下initialize方法和start方法
          initialize方法:
          這里面只有一個(gè)主要任務(wù),就是逐次調(diào)用server中所有定義的service的initialize方法,
          而每個(gè)service的initialize方法中調(diào)用這個(gè)service中定義的所有connector的initialize方法,
          而connector的initialize方法則是創(chuàng)建一個(gè)serversocket用于接受客戶端的請(qǐng)求就結(jié)束了。
          如果大家看一下tomcat下面conf/server.xml,就可以發(fā)現(xiàn),tomcat默認(rèn)只定義了一個(gè)service叫做Tomcat-Standalone,
          而下面只有默認(rèn)定義了3個(gè)connector:
          1. 8080端口的http connector
          2. 8443端口的http ssl connector
          3. 8009端口的Coyote/JK2 AJP 1.3 Connector
          我想大家對(duì)這3個(gè)端口一定不陌生吧。
          start方法:
          這個(gè)方法里面有一個(gè)tomcat很重要,也是我認(rèn)為tomcat設(shè)計(jì)對(duì)一個(gè)亮點(diǎn),就是Lifecycle這個(gè)東東,它很象一個(gè)bus(總線)。
          我想大家進(jìn)行過(guò)程序設(shè)計(jì)的一定知道,開始設(shè)計(jì)的時(shí)候總要根據(jù)一個(gè)原則分出幾個(gè)模塊來(lái),是為了代碼分塊,或者將
          一部分功能相似的代碼組織成一個(gè)模塊,這樣比較清楚,例如一個(gè)進(jìn)銷存系統(tǒng)會(huì)有采購(gòu),銷售,庫(kù)存和財(cái)務(wù)等模塊,但是
          我想很多人也碰到過(guò)這樣的情況就是雖然分了模塊但是如果在開發(fā)完畢以后,另外一個(gè)客戶說(shuō)只想要其中的銷售模塊,我想
          大部分的開發(fā)人員肯定傻眼,因?yàn)殡m然當(dāng)時(shí)設(shè)計(jì)的時(shí)候分了模塊,但是這些模塊編寫的時(shí)候卻是交織在一起,互相的接口定義
          很模糊,基本上都是直接調(diào)用另一個(gè)模塊的方法,這樣肯定分不開。而tomcat的這個(gè)Lifecycle的設(shè)計(jì)理念就可以解決這個(gè)問題的
          一部分,它的原理就象是一個(gè)bus(總線),例如一個(gè)模塊做完一個(gè)動(dòng)作以后,例如銷售模塊創(chuàng)建好一個(gè)訂單后,本來(lái)要直接調(diào)用
          庫(kù)存模塊的api鎖住一部分庫(kù)存(我只是隨便舉個(gè)例子,實(shí)際業(yè)務(wù)不一定是這樣),這樣銷售模塊就需要依賴庫(kù)存模塊了。但是使用了
          bus方式。我們就可以在訂單創(chuàng)建后,向bus上發(fā)送一個(gè)訂單創(chuàng)建的消息,而總線有一個(gè)事件注冊(cè)機(jī)制,有點(diǎn)象swing的event,listener,
          例如庫(kù)存模塊有一個(gè)listener專門用于監(jiān)聽訂單創(chuàng)建的消息,進(jìn)行處理,這樣2個(gè)模塊就互不依賴了。有興趣的兄喜可以看看jcp上面
          的一個(gè)叫做infobus的專題。
          當(dāng)然這個(gè)方式只是解決有效降低模塊偶合度的一個(gè)方面(因?yàn)橛械臅r(shí)候必須要直接調(diào)用另外一個(gè)模塊的接口,
          例如庫(kù)存模塊一定要直接缺德一個(gè)銷售訂單的信息,那么就需要定義一個(gè)接口類來(lái)描述訂單的詳細(xì)信息啦,這里就不具體解釋了,
          有空可以專門發(fā)個(gè)帖子跟大家探討這個(gè)問題:-) ),就是不要顯式觸發(fā)另一個(gè)模塊的某個(gè)動(dòng)作,而是通過(guò)bus機(jī)制來(lái)發(fā)送消息,
          而每個(gè)模塊都有一個(gè)自己的handler,會(huì)監(jiān)聽bus,對(duì)自感興趣的事件進(jìn)行處理。tomcat的Lifecycle就是這個(gè)東西。
          下面再回到start方法:
          1. 它首先向總線發(fā)送了2個(gè)事件:BEFORE_START_EVENT和START_EVENT
          2. 然后調(diào)用每個(gè)service的start方法,最后發(fā)送AFTER_START_EVENT消息通知其他程序
          而service的start方法主要進(jìn)行的動(dòng)作如下:
          1. 發(fā)送BEFORE_START_EVENT消息
          2. 調(diào)用container的start方法
          3. 然后調(diào)用connector的start方法
          4. 最后發(fā)送AFTER_START_EVENT消息.
          而connector的start方法就是大家最熟悉的socket編程了,大家可以參看org.apache.catalina.connector.http.HttpConnector這個(gè)類,
          主要是使用java里面的多線程操作,初始化一個(gè)HttpProcessor的線程池,然后通過(guò)wait方法阻塞住每個(gè)HttpProcessor線程,只有
          當(dāng)接受到一個(gè)http請(qǐng)求時(shí),在通過(guò)notify方法激活HttpProcessor線程,讓其處理用戶的http請(qǐng)求。

          到此為止主要簡(jiǎn)單介紹了一下tomcat 4.1.30的啟動(dòng)過(guò)程,下次有機(jī)會(huì)的話,可以再看看它的webapp的deploy的管理部分的代碼,然后和大家分享。如果大家對(duì)我寫的帖子有什么意見的話,也歡迎批評(píng)指正,希望感興趣的兄弟可以一起探討:-) 廢話不說(shuō)了,很晚了該睡覺了,祝大家周一工作愉快

          posted on 2006-09-13 20:12 ericli 閱讀(250) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 镇原县| 漯河市| 丹东市| 游戏| 龙陵县| 盐源县| 泰顺县| 顺义区| 安泽县| 泰安市| 临城县| 鄂托克前旗| 柏乡县| 丰镇市| 安泽县| 永清县| 营口市| 运城市| 大兴区| 雅江县| 吴旗县| 志丹县| 甘洛县| 东辽县| 绥阳县| 新宁县| 鹤岗市| 克东县| 修文县| 无棣县| 科尔| 建宁县| 达拉特旗| 平远县| 华亭县| 泾阳县| 乡宁县| 民县| 营山县| 纳雍县| 墨脱县|