日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
分享Tomcat源碼系列三部曲

最近在看Tomcat的源碼,下面用博客記下看源碼的一些心得。

10年積累的做網(wǎng)站、網(wǎng)站建設(shè)經(jīng)驗,可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認識你,你也不認識我。但先網(wǎng)站設(shè)計后付款的網(wǎng)站建設(shè)流程,更有恭城免費網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

Tomcat是從org.apache.catalina.startup.Bootstrap#main()開始啟動. 大致分為三個步驟,即init、load和start。代碼如下:

Java代碼

 
 
 
 
  1. public static void main(String args[]) {      
  2.         try {      
  3.             // Attempt to load JMX class      
  4.             new ObjectName("test:foo=bar");      
  5.         } catch (Throwable t) {      
  6.             System.out.println(JMX_ERROR_MESSAGE);      
  7.             try {      
  8.                 // Give users some time to read the message before exiting      
  9.                 Thread.sleep(5000);      
  10.             } catch (Exception ex) {      
  11.             }      
  12.             return;      
  13.         }      
  14.         if (daemon == null) {      
  15.             daemon = new Bootstrap();      
  16.             try {      
  17.                 daemon.init();   1      
  18.             } catch (Throwable t) {      
  19.                 t.printStackTrace();      
  20.                 return;      
  21.             }      
  22.         }      
  23.         try {      
  24.             String command = "start";      
  25.             if (args.length > 0) {      
  26.                 command = args[args.length - 1];      
  27.             }      
  28.             if (command.equals("startd")) {      
  29.                 args[0] = "start";      
  30.                 daemon.load(args);      
  31.                 daemon.start();      
  32.             } else if (command.equals("stopd")) {      
  33.                 args[0] = "stop";      
  34.                 daemon.stop();      
  35.             } else if (command.equals("start")) {      
  36.                 daemon.setAwait(true);      
  37.                 daemon.load(args);   2      
  38.     
  39.              // 反射調(diào)用Catalina的start方法      
  40.                 daemon.start();        3      
  41.             } else if (command.equals("stop")) {      
  42.                 daemon.stopServer(args);      
  43.             }      
  44.         } catch (Throwable t) {      
  45.             t.printStackTrace();      
  46.         }      
  47.     }      

 

從以上可以很清楚的看出tomcat是通過參數(shù)的不同進行相應(yīng)的命令調(diào)用。

1 啟動、初始化(加載類)

啟動之前要進行相應(yīng)的init()初始化,進行相應(yīng)的環(huán)境設(shè)置以及包的加,以下是init()方法。(org.apache.catalina.startup.Bootstrap.init())

Java代碼

 

 
 
 
 
  1. public void init()      
  2.         throws Exception      
  3.     {      
  4.         setCatalinaHome();//設(shè)置Catalina安裝目錄      
  5.         setCatalinaBase();//設(shè)置Catalina工作目錄      
  6.         initClassLoaders();//加載jar包      
  7.     
  8.        // 將classload設(shè)置進線程,以便我們使用時進行調(diào)用            
  9.         Thread.currentThread().      
  10.                       setContextClassLoader(catalinaLoader);      
  11.         SecurityClassLoad.securityClassLoad(catalinaLoader);      
  12.     
  13.         // 加載啟動類和調(diào)用它的process方法      
  14.         if (log.isDebugEnabled())      
  15.             log.debug("Loading startup class");      
  16.         Class startupClass =      
  17.             catalinaLoader.loadClass      
  18.             ("org.apache.catalina.startup.Catalina");      
  19.         Object startupInstance = startupClass.newInstance();      
  20.     
  21.         // 設(shè)置共享擴張類加載器      
  22.         if (log.isDebugEnabled())      
  23.             log.debug("Setting startup class properties");      
  24.         String methodName = "setParentClassLoader";      
  25.         Class paramTypes[] = new Class[1];      
  26.         paramTypes[0] = Class.forName("java.lang.ClassLoader");      
  27.         Object paramValues[] = new Object[1];      
  28.         paramValues[0] = sharedLoader;      
  29.         Method method =      
  30.         startupInstance.getClass().getMethod(methodName,      
  31.                                                           paramTypes);      
  32.         method.invoke(startupInstance, paramValues);      
  33.         catalinaDaemon = startupInstance;      
  34.     }  

 

在加載jar的時候,需要初始化classloader,代碼如下:(org.apache.catalina.startup.Bootstrap)

Java代碼

 

 
 
 
 
  1. private void initClassLoaders() {      
  2.         try {      
  3.             commonLoader = createClassLoader("common", null);      
  4.             catalinaLoader= createClassLoader("server", commonLoader);      
  5.             sharedLoader = createClassLoader("shared", commonLoader);      
  6.         } catch (Throwable t) {      
  7.             log.error("Class loader creation threw exception", t);      
  8.             System.exit(1);      
  9.         }      
  10.     }   

 

tomcat中的加載方式是:

|-------commonLoader (common)-> System Loader

|-------sharedLoader (shared)-> commonLoader -> System Loader

|-------catalinaLoader(server) -> commonLoader -> System Loader

Common是公共類加載器,負責加載tomcat內(nèi)部和web應(yīng)用程序可以看到的類(%CATALINA_HOME%/bin/common下的jar文件),Catalina負責加載的是tomcat內(nèi)部使用的類(%CATALINA_HOME%/server下的jar文件),這些類對web應(yīng)用程序不可見。Shared負責加載的是web應(yīng)用程序之間共享的類(%CATALINA_BASE%/shared下的jar文件),這些類對于tomcat內(nèi)部是不可見的。如果%CATALINA_HOME%/conf/catalina.Properties中沒有指定Common的搜索路徑,則用當前的類的類加載器即系統(tǒng)類加載器作為Common。

2 裝載相應(yīng)的資源

下面主要講解tomcat的load()方法。下圖是Catalina.load方法的時序圖。

 

 

(1) 從上面的時序圖可以看出首先調(diào)用Catalina類的load()方法,具體代碼如下:

(org.apache.catalina.startup.Catalina)。

Java代碼

 

 
 
 
 
  1. public void load() {      
  2.         initDirs();      
  3.     
  4.         // Before digester - it may be needed      
  5.         initNaming();      
  6.     
  7.         // Create and execute our Digester      
  8.         Digester digester = createStartDigester();      
  9.             
  10.         try {      
  11.             inputSource.setByteStream(inputStream);      
  12.             digester.push(this);      
  13.             digester.parse(inputSource); //對server.xml進行解析      
  14.             inputStream.close();      
  15.         }      
  16.        ......      
  17.         // Start the new server      
  18.         if (server instanceof Lifecycle) {      
  19.             try {      
  20.                 server.initialize();  //server初始化工作      
  21.             } catch (LifecycleException e) {      
  22.                 log.error("Catalina.start", e);      
  23.             }      
  24.         }      
  25.         long t2 = System.currentTimeMillis();      
  26.         log.info("Initialization processed in " + (t2 - t1) + " ms");      
  27.     
  28.     }     

 

(2) 在上面的load()方法中需要進行server的初始化工作,下圖為Catalina.initialize的時序圖,從圖中可以看出server初始化所完成的工作。

 

 

至此,load方法結(jié)束,初期化的工作結(jié)束,下面開始進入start方法。

3 容器啟動

容器啟動時,會調(diào)用Catalina.start(),下圖為它的時序圖。從圖中可以看出StandardService的start方法被調(diào)用后會分別對Container和Connector進行start方法的調(diào)用。

 

 

1. Bootstrap調(diào)用Catalina的start方法

Catalina.start()方法(org.apache.catalina.startup.Catalina.start())

Java代碼

 

 
 
 
 
  1. public void start() {      
  2.     // 啟動server      
  3.     if (server instanceof Lifecycle) {      
  4.         try {      
  5.             ((Lifecycle) server).start();      
  6.                     ......      
  7.    }     

 

2. Catalina調(diào)用StandardServer的start方法

StandardServer.start() (org.apache.catalina.core.StandardServer.start() )

Java代碼

 

 
 
 
 
  1. public void start() throws LifecycleException {            
  2.         synchronized (services) {      
  3.             for (int i = 0; i < services.length; i++) {      
  4.                 if (services[i] instanceof Lifecycle)      
  5.                     ((Lifecycle) services[i]).start();      
  6.             }       
  7. }   

 

3. StandardServer調(diào)用StandardService的start方法

Java代碼

 

 
 
 
 
  1. org.apache.catalina.core.StandardService.start() )      
  2.         public void start() throws LifecycleException {      
  3.                   if (container != null) {      
  4.             synchronized (container) {      
  5.                 if (container instanceof Lifecycle) {      
  6.               //  standardEngine的啟動      
  7.                     ((Lifecycle) container).start();      
  8.                 }      
  9.             }      
  10.        //兩個connector的啟動,8080和8009        
  11.        synchronized (connectors) {        
  12.            for (int i = 0; i < connectors.length; i++) {        
  13.                if (connectors[i] instanceof Lifecycle)        
  14.                    ((Lifecycle) connectors[i]).start();        
  15.                   }        
  16.        }        
  17. }  

 

以上StandardService.start()方法主要實現(xiàn)了兩個功能,standardEngine的啟動和connector的啟動,下面分別來介紹。

#p#

下面是standardEngine的啟動和connector的啟動

● standardEngine的啟動

(1) 首先是StandardEngine.start()被調(diào)用

Java代碼

 

 

 
 
 
 
  1. public void start() throws LifecycleException {      
  2.        // Standard container startup        
  3.       //進行l(wèi)ogger,manager,cluster,realm,resource的啟動        
  4.        super.start();      
  5. }  

 

(2) super.start()--->org.apache.catalina.core.ContainerBase#start()

Java代碼

 

 

 
 
 
 
  1. public synchronized void start() throws LifecycleException {      
  2. //(省略)  server.xml中配置應(yīng)用組件的啟動         
  3. //StandardHost容器的啟動,        
  4.         Container children[] = findChildren();        
  5.         for (int i = 0; i < children.length; i++) {        
  6.             if (children[i] instanceof Lifecycle)        
  7.                 ((Lifecycle) children[i]).start();        
  8.         }          
  9.     
  10.     //StandardPipeline的啟動(容器與容器間的管道)        
  11.         if (pipeline instanceof Lifecycle)        
  12.             ((Lifecycle) pipeline).start();       
  13. }   

 

(3) StandardHost.start()被調(diào)用

Java代碼

 

 

 
 
 
 
  1. public synchronized void start() throws LifecycleException {      
  2. //返回到以上的containerBase#start執(zhí)行pipeline        
  3.       super.start();       
  4. }  

 

(4) StandardPipeline#start

Java代碼

 

 

 
 
 
 
  1. public synchronized void start() throws LifecycleException {      
  2.        // 將會調(diào)用HostConfig#start方法        
  3.        lifecycle.fireLifecycleEvent(START_EVENT, null);        
  4.        
  5.        // Notify our interested LifecycleListeners        
  6.        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);        
  7. }    

 

(5) HostConfig#start

Java代碼

 

 

 
 
 
 
  1. public void start() {       
  2.           //部暑webapps        
  3.           deployApps();                 
  4.   }     

 

(6) HostConfig#deployApps

Java代碼

 

 

 
 
 
 
  1. protected void deployApps() {        
  2.     File appBase = appBase();        
  3.     File configBase = configBase();        
  4.     // Deploy XML descriptors from configBase        
  5.     deployDescriptors(configBase, configBase.list());        
  6.     // Deploy WARs, and loop if additional descriptors are found        
  7.     deployWARs(appBase, appBase.list());        
  8.     // Deploy expanded folders        
  9.     deployDirectories(appBase, appBase.list());                  
  10. }    

 

(7) deployWARs

Java代碼

 

 

 
 
 
 
  1. protected void deployWARs(File appBase, String[] files) {      
  2. ……      
  3. deployWAR(contextPath, dir, file);               
  4.   }  

 

(8) deployWAR

Java代碼

 

 

 
 
 
 
  1. protected void deployWAR(String contextPath, File war, String file) {      
  2. if (context instanceof Lifecycle) {        
  3.   // (省略)      
  4.             Class clazz = Class.forName(host.getConfigClass());        
  5.             LifecycleListener listener =        
  6.                 (LifecycleListener) clazz.newInstance();        
  7.             ((Lifecycle) context).addLifecycleListener(listener);        
  8.         }        
  9.         context.setPath(contextPath);        
  10.         context.setDocBase(file);        
  11.         //以下這一步跟進去,,StandardContext的啟動        
  12.         host.addChild(context);              
  13.   }   

 

(9) StandardContext#start

在Context的啟動過程中,主要完成了以下任務(wù)。

----------------------------------------------------------------------------------------------------------------------

a) 設(shè)置web app的具體目錄webappResources。

b) postWorkDirectory (),創(chuàng)建臨時文件目錄。Tomcat下面有一個work目錄,用來存放臨時文件。

c) 觸發(fā)START_EVENT事件監(jiān)聽,在這個事件監(jiān)聽里面會啟動ContextConfig的start()事件,ContextConfig是用來配置web.xml的。

d) 為context創(chuàng)建welcome files,通常是這三個啟動文件:index.html、index.htm、index.jsp

e) 配置filter

f) 啟動帶有 的Servlet。

g) 注冊JMX。

----------------------------------------------------------------------------------------------------------------------

至此,Container啟動完畢,下面是connector的啟動。

● connector的啟動

(1) org.apache.catalina.connector.Connector.start()

Java代碼

 

 

 
 
 
 
  1. public void start() throws LifecycleException {      
  2.            // Http11Protocol的啟動      
  3.             protocolHandler.start();      
  4. }   

 

(2) Http11Protocol#start

Java代碼

 

 

 
 
 
 
  1. public void start() throws Exception {      
  2. try {        
  3.             //到了終點的啟動        
  4.             endpoint.start();        
  5.         } catch (Exception ex) {        
  6.             log.error(sm.getString("http11protocol.endpoint.starterror"), ex);        
  7.             throw ex;        
  8.         }    

 

(3) JIoEndPoint#start

Java代碼

 

 

 
 
 
 
  1. public void start()        
  2.         throws Exception {                 
  3.                    
  4.             for (int i = 0; i < acceptorThreadCount; i++) {        
  5.         //這里的acceptor是一個線程,里面是一個serversocket的啟動        
  6.                 Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);        
  7.                 acceptorThread.setPriority(threadPriority);        
  8.                 acceptorThread.setDaemon(daemon);        
  9.                 acceptorThread.start();        
  10.             }        
  11.         }  

 

(4) Acceptor#run

Java代碼

 

 

 
 
 
 
  1. public void run() {                    
  2. // Accept the next incoming connection from the server socket        
  3.                try {        
  4.           //這里進行了accept(),等待客戶端消息,進行接收        
  5.                    Socket socket = serverSocketFactory.acceptSocket(serverSocket);        
  6.                    serverSocketFactory.initSocket(socket);        
  7.                    // Hand this socket off to an appropriate processor        
  8.                    if (!processSocket(socket)) {        
  9.                        // Close socket right away        
  10.                        try {        
  11.                            socket.close();        
  12.                        } catch (IOException e) {        
  13.                            // Ignore        
  14.                        }        
  15.                    }        
  16.                }catch ( IOException x ) {        
  17.                    if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);        
  18.                } catch (Throwable t) {        
  19.                    log.error(sm.getString("endpoint.accept.fail"), t);        
  20.                }        
  21. }    

 

至此Connector.start方法調(diào)用完畢。整個server啟動完畢。

#p#

本次講解一下Tomcat請求處理的流程,不當之處還請comment。

一. Tomcat 總體結(jié)構(gòu)

Tomcat采用模塊化管理,下面是 Tomcat 的總體結(jié)構(gòu)圖:

 

 

從上圖中可以看出 Tomcat 的核心是兩個組件:Connector 和 Container。下面是一些概念的介紹。

① Server

一個server代表了整個catalina servlet容器,在Tomcat里面的Server的用處是啟動和監(jiān)聽服務(wù)端事件(諸如重啟、關(guān)閉等命令)。

② Service

Service是由一個或多個Connector與一個Engine的組合。

③ Connector

Connector將在某個指定的端口上監(jiān)聽客戶的請求,把從socket傳遞過來的數(shù)據(jù),封裝成Request,傳遞給Engine來處理,并從Engine處獲得響應(yīng)并返回給客戶。

Tomcat通常會用到兩種Connector:

a) Http Connector 在端口8080處偵聽來自客戶browser的http請求。

b) AJP Connector 在端口8009處偵聽來自其它WebServer(Apache)的servlet/jsp代理請求。

二、請求處理過程解析

1. Connector處理請求

Connector處理請求的流程大致如下:

 

 

Connector組件啟動后,會偵聽相關(guān)的端口的客戶端請求。

(1) 接受一個新的連接請求(org.apache.tomcat.util.net.TcpWorkerThread)

Java代碼

 

 
 
 
 
  1. void runIt(Object[] perThrData){      
  2.        Socket s = null;      
  3.             try {      
  4.                 s = endpoint.acceptSocket();  //獲取一個請求      
  5.             } finally {      
  6.                 if (endpoint.isRunning()) {      
  7.                     endpoint.tp.runIt(this);      
  8.   // 此處啟動另一個TcpWorkerTread去接受其他請求,此線程處理已接受的請求      
  9.                 }      
  10.             }                      
  11.       TcpConnection con = null;      
  12.       con = (TcpConnection) perThrData[0];      
  13.       con.setEndpoint(endpoint);      
  14.       con.setSocket(s);endpoint.getConnectionHandler().processConnection(con,(Object[]) perThrData[1]);          
  15. }    

 

(2) 新接收的請求被傳到Http11ConnectionHandler中處理。(org.apache.coyote.http11.Http11Protocol.Http11ConnectionHandler)

Java代碼

 

 
 
 
 
  1. void processConnection(TcpConnection connection, Object[] thData){          
  2.     Http11Processor  processor=null;      
  3.     processor=(Http11Processor)thData[Http11Protocol.THREAD_DATA_PROCESSOR];        
  4.     socket=connection.getSocket();                           
  5.     InputStream in = socket.getInputStream();        
  6.     OutputStream out = socket.getOutputStream();      
  7.     processor.setSocket(socket );      
  8.     processor.process(in, out);        
  9. //processor是org.apache.coyote.http11.Http11Processor 的 一個實例      
  10. }    

 

(3) 在 Http11Processor 中處理 http11 協(xié)議相關(guān)的信息(org.apache.coyote.http11.Http11Processor)

Java代碼

 

 
 
 
 
  1. void process(InputStream input, OutputStream output) throws IOException{      
  2.         ~~略~~      
  3.         inputBuffer.setInputStream(input);      
  4.         outputBuffer.setOutputStream(output);      
  5.         inputBuffer.parseHeaders();      
  6.       //http11 協(xié)議頭在此方法中被取出      
  7.         adapter.service(request, response);         
  8.       //adapter 是org.apache.catalina.connector.CoyoteAdapter 的 一個實例      
  9. }   

 

接下來的流程交由容器進行處理。

2. 容器處理請求

容器交由Pipeline處理,這個Pipeline里面會放置一些vavle,請求沿著pipeline傳遞下去并且vavle對其進行相關(guān)的處理。比如說日志等,valve還可以自定義,具體需要查看server.xml配置文件。相關(guān)類圖如下:

 

 

Tomcat的主要處理組件Engine、Host、Context和Wrapper的實現(xiàn)都會實現(xiàn)Pipeline接口,實際對請求的處理是一個Adpater,Tomcat中Adapter的實現(xiàn)是CoyoteAdapter,因此容器請求處理的入口是CoyoteAdapter的service方法。

1. CoyoteAdapter.service

--組裝好請求處理鏈

--StandardEngine. getPipeline().getFirst().invoke(request, response);

--StandardEngineValve.invoke

2. StandardEngineValve.invoke

--Host.getPipeline().getFirst().invoke(request, response);

--StandardHostValve.invoke

3. StandardHostValve.invoke

--Context. getPipeline().getFirst().invoke(request, response);

--StandardContextValve.invoke

4. StandardContextValve.invoke

--ServletRequestListener.requestInitialized

--Wrapper.getPipeline().getFirst().invoke(request, response);

--StandardWrapperValve.invoke

-- ServletRequestListener.requestDestroyed

5. StandardWrapperValve.invoke

--組裝Filter+Servlet

--處理請求

(1) Connector傳來的請求調(diào)用CoyoteAdapter.service()方法。(org.apache.catalina.connector.CoyoteAdapter)

Java代碼

 

 
 
 
 
  1. public void service(org.apache.coyote.Request req,         
  2.                     org.apache.coyote.Response res)        
  3.     throws Exception {        
  4.          ~~略~~       
  5.    if (request == null) {       
  6.         request = (Request) connector.createRequest();      
  7.         request.setCoyoteRequest(req);      
  8.         response = (Response) connector.createResponse();      
  9.      response.setCoyoteResponse(res);      
  10.      //創(chuàng)建request、response對象        
  11.          ~~略~~        
  12.     }              
  13.     try {         
  14.         if (postParseRequest(req, request, res, response)) {        
  15. connector.getContainer().getPipeline().getFirst().invoke(request, response);      
  16. //此處的Container是StandardEngine對象       
  17.            ~~略~~         
  18.     }        
  19. }    

 

(2) 默認StandardEngine的Pipeline會有StandardEngineValve處理單元(參照StandardEngine構(gòu)造函數(shù))。(org.apache.catalina.core.StandardEngineValve)

Java代碼

 

 
 
 
 
  1. public final void invoke(Request request, Response response)        
  2.     throws IOException, ServletException {          
  3.      // Select the Host to be used for this Request        
  4.   Host host = request.getHost();        
  5.     if (host == null) {       &nb
    新聞名稱:分享Tomcat源碼系列三部曲
    網(wǎng)站地址:http://m.5511xx.com/article/dpdoshs.html