爪哇東南的自留地

          學習探討開源和web開發

          導航

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

          統計

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          相冊

          收藏夾

          life

          technique

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          tomcat 4.1.30啟動過程的源碼分析

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

          一. 啟動類(包含main()方法的類):
          org.apache.catalina.startup.Bootstrap
          這個類是tomcat的啟動類,主要按照如下步驟,進行主要的啟動工作:
          1. 創建3個ClassLoader:common,catalina和share,它們對應tomcat的3個Classloader,我想對tomcat
          的classloader有研究的兄弟對這個肯定不陌生,其中common classloader是緊跟在系統的classloader(也就是
          系統環境變量中設置的CLASSPATH所對應的classloader),而catalina classloader是common的子classloader,是tomcat
          運行所需要的類的classloader,而shared classloader也是common的子classloader,是和catalina平級的classloader,
          之所以說是shared classloader,是因為它是所有tomcat下面發布的webapp classloader(每一個web app都有一個自己的classloader)
          的父classloader。它們這3個classloader分別讀取tomcat home下面的common, server和shared三個目錄里面的classes和lib目錄,
          用于初始化自己所控制的類庫和資源。

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

          二.org.apache.catalina.startup.Catalina類
          現在程序轉到org.apache.catalina.startup.Catalina類里面的process方法。
          這個方法首先設置一下catalina的home和base目錄,然后通過arguments方法解析命令行參數,
          最后調用execute()方法啟動server。而execute方法很簡單,就是根據arguments解析的命令行參數,
          決定是啟動server,還是stop server,如果是start server,就調用start方法,而下面重點講一下這個start()方法,
          因為才算是一個真正開始的啟動tomcat的地方:-)
          1. start方法首先使用Digester(這個東東是jakarta commons里面的一個用于解析xml文件的工具包,一開始是專門用于解析struts配置文件的,
          后來被抽象成現在的一個通用工具,主要還是用來解析xml配置文件,根據一些定義的rule自動生成對應的類的實例,具體信息可以參考
          apache網站上的文檔)來設置tomcat配置文件,也就是/conf/server.xml這個文件的解析規則
          然后通過如下代碼來將配置文件中的數據轉化成內存中的實例:

          代碼:
          ??????? 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);

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


          轉換的規則如下(我只作一些簡單的介紹),例如配置文件中的
          a. Server對應可以產成一個org.apache.catalina.core.StandardServer類(這個類很重要,是tomcat server的實現)
          b. Server/GlobalNamingResources對應生成org.apache.catalina.deploy.NamingResources類
          而大家比較熟悉的監聽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
          這個類里面的createStartDigester方法.
          在這段代碼之后,一個叫server的變量已經通過Digester工具生成了,它將會用于啟動tomcat。
          2. 然后程序進行了一些server啟動前的設置工作,例如重定向log輸出流等等。而server啟動的代碼如下:

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

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

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


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 汝州市| 略阳县| 泰兴市| 温泉县| 昌平区| 澄江县| 澄城县| 榕江县| 西乌珠穆沁旗| 吉林省| 垦利县| 海林市| 剑阁县| 湟中县| 大兴区| 凉山| 永城市| 同心县| 博湖县| 平昌县| 临潭县| 日喀则市| 本溪| 米易县| 永新县| 岢岚县| 阿克苏市| 永年县| 衡阳市| 钟祥市| 侯马市| 囊谦县| 龙海市| 宝坻区| 卫辉市| 紫阳县| 锡林浩特市| 云林县| 友谊县| 夏邑县| 崇阳县|