tomcat(一个牛人写的文章,自己看)

Tomcat源码系列1--Tomcat启动流程1

文章分类:Java编程

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

Tomcat是从org.apache.catalina.startup.Bootstrap#main()开始启动. 大致分为三个步骤,即init、load和start。代码如下: 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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.              // 反射调用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.     }    
public static void main(String args[]) { 
        try { 
            // Attempt to load JMX class 
            new ObjectName("test:foo=bar"); 
        } catch (Throwable t) { 
            System.out.println(JMX_ERROR_MESSAGE); 
            try { 
                // Give users some time to read the message before exiting 
                Thread.sleep(5000); 
            } catch (Exception ex) { 
            } 
            return; 
        } 
        if (daemon == null) { 
            daemon = new Bootstrap(); 
            try { 
                daemon.init();   ★1 
            } catch (Throwable t) { 
                t.printStackTrace(); 
                return; 
            } 
        } 
        try { 
            String command = "start"; 
            if (args.length > 0) { 
                command = args[args.length - 1]; 
            } 
            if (command.equals("startd")) { 
                args[0] = "start"; 
                daemon.load(args); 
                daemon.start(); 
            } else if (command.equals("stopd")) { 
                args[0] = "stop"; 
                daemon.stop(); 
            } else if (command.equals("start")) { 
                daemon.setAwait(true); 
                daemon.load(args);   ★2 

             // 反射调用Catalina的start方法 
                daemon.start();        ★3 
            } else if (command.equals("stop")) { 
                daemon.stopServer(args); 
            } 
        } catch (Throwable t) { 
            t.printStackTrace(); 
        } 
    }  

  
从以上可以很清楚的看出tomcat是通过参数的不同进行相应的命令调用。
★1 启动、初始化(加载类)
启动之前要进行相应的init()初始化,进行相应的环境设置以及包的加,以下是init()方法。(org.apache.catalina.startup.Bootstrap.init())

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. public   void  init()   
  2.         throws  Exception   
  3.     {   
  4.         setCatalinaHome();//设置Catalina安装目录    
  5.         setCatalinaBase();//设置Catalina工作目录    
  6.         initClassLoaders();//加载jar包    
  7.   
  8.        // 将classload设置进线程,以便我们使用时进行调用          
  9.         Thread.currentThread().   
  10.                       setContextClassLoader(catalinaLoader);   
  11.         SecurityClassLoad.securityClassLoad(catalinaLoader);   
  12.   
  13.         // 加载启动类和调用它的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.         // 设置共享扩张类加载器    
  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.     }   
public void init() 
        throws Exception 
    { 
        setCatalinaHome();//设置Catalina安装目录 
        setCatalinaBase();//设置Catalina工作目录 
        initClassLoaders();//加载jar包 

       // 将classload设置进线程,以便我们使用时进行调用       
        Thread.currentThread(). 
                      setContextClassLoader(catalinaLoader); 
        SecurityClassLoad.securityClassLoad(catalinaLoader); 

        // 加载启动类和调用它的process方法 
        if (log.isDebugEnabled()) 
            log.debug("Loading startup class"); 
        Class startupClass = 
            catalinaLoader.loadClass 
            ("org.apache.catalina.startup.Catalina"); 
        Object startupInstance = startupClass.newInstance(); 

        // 设置共享扩张类加载器 
        if (log.isDebugEnabled()) 
            log.debug("Setting startup class properties"); 
        String methodName = "setParentClassLoader"; 
        Class paramTypes[] = new Class[1]; 
        paramTypes[0] = Class.forName("java.lang.ClassLoader"); 
        Object paramValues[] = new Object[1]; 
        paramValues[0] = sharedLoader; 
        Method method = 
        startupInstance.getClass().getMethod(methodName, 
                                                          paramTypes); 
        method.invoke(startupInstance, paramValues); 
        catalinaDaemon = startupInstance; 
    } 

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

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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.     }   
private void initClassLoaders() { 
        try { 
            commonLoader = createClassLoader("common", null); 
            catalinaLoader= createClassLoader("server", commonLoader); 
            sharedLoader = createClassLoader("shared", commonLoader); 
        } catch (Throwable t) { 
            log.error("Class loader creation threw exception", t); 
            System.exit(1); 
        } 
    } 

 

tomcat中的加载方式是:
|-------commonLoader (common)-> System Loader
|-------sharedLoader (shared)-> commonLoader -> System Loader
|-------catalinaLoader(server) -> commonLoader -> System Loader

Common 是公共类加载器,负责加载tomcat内部和web应用程序可以看到的类(%CATALINA_HOME%/bin/common下的jar文 件),Catalina负责加载的是tomcat内部使用的类(%CATALINA_HOME%/server下的jar文件),这些类对web应用程序 不可见。Shared负责加载的是web应用程序之间共享的类(%CATALINA_BASE%/shared下的jar文件),这些类对于tomcat 内部是不可见的。如果%CATALINA_HOME%/conf/catalina.Properties中没有指定Common的搜索路径,则用当前的 类的类加载器即系统类加载器作为Common。  

★2 装载相应的资源
下面主要讲解tomcat的load()方法。下图是Catalina.load方法的时序图。

tomcat(一个牛人写的文章,自己看)
 
(1) 从上面的时序图可以看出首先调用Catalina类的load()方法,具体代码如下:
(org.apache.catalina.startup.Catalina)。

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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.     }   
public void load() { 
        initDirs(); 

        // Before digester - it may be needed 
        initNaming(); 

        // Create and execute our Digester 
        Digester digester = createStartDigester(); 
       
        try { 
            inputSource.setByteStream(inputStream); 
            digester.push(this); 
            digester.parse(inputSource); //对server.xml进行解析 
            inputStream.close(); 
        } 
       ...... 
        // Start the new server 
        if (server instanceof Lifecycle) { 
            try { 
                server.initialize();  //server初始化工作 
            } catch (LifecycleException e) { 
                log.error("Catalina.start", e); 
            } 
        } 
        long t2 = System.currentTimeMillis(); 
        log.info("Initialization processed in " + (t2 - t1) + " ms"); 

    } 

 
(2) 在上面的load()方法中需要进行server的初始化工作,下图为Catalina.initialize的时序图,从图中可以看出server初始化所完成的工作。

tomcat(一个牛人写的文章,自己看)
 

至此,load方法结束,初期化的工作结束,下面开始进入start方法。

★3 容器启动
容器启动时,会调用Catalina.start(),下图为它的时序图。从图中可以看出StandardService的start方法被调用后会分别对Container和Connector进行start方法的调用。


tomcat(一个牛人写的文章,自己看)
 

1. Bootstrap调用Catalina的start方法
Catalina.start()方法(org.apache.catalina.startup.Catalina.start())

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. public   void  start() {   
  2.     // 启动server    
  3.     if  (server  instanceof  Lifecycle) {   
  4.         try  {   
  5.             ((Lifecycle) server).start();   
  6.                     ......   
  7.    }   
    public void start() { 
        // 启动server 
        if (server instanceof Lifecycle) { 
            try { 
                ((Lifecycle) server).start(); 
                        ...... 
       } 

 
2. Catalina调用StandardServer的start方法
StandardServer.start() (org.apache.catalina.core.StandardServer.start() ) 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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. }   
public void start() throws LifecycleException {       
        synchronized (services) { 
            for (int i = 0; i < services.length; i++) { 
                if (services[i] instanceof Lifecycle) 
                    ((Lifecycle) services[i]).start(); 
            }  
} 

 

3. StandardServer调用StandardService的start方法

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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. }   
org.apache.catalina.core.StandardService.start() ) 
        public void start() throws LifecycleException { 
                  if (container != null) { 
            synchronized (container) { 
                if (container instanceof Lifecycle) { 
              //  standardEngine的启动 
                    ((Lifecycle) container).start(); 
                } 
            } 
       //两个connector的启动,8080和8009   
       synchronized (connectors) {   
           for (int i = 0; i < connectors.length; i++) {   
               if (connectors[i] instanceof Lifecycle)   
                   ((Lifecycle) connectors[i]).start();   
                  }   
       }   
} 

 
以上StandardService.start()方法主要实现了两个功能,standardEngine的启动和connector的启动,下面分别来介绍。

 

 

Tomcat源码系列2--Tomcat启动流程2

文章分类:Java编程

下面是standardEngine 的启动和 connector 的启动

 

● standardEngine的启动
(1) 首先是StandardEngine.start()被调用

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. public   void  start()  throws  LifecycleException {   
  2.        // Standard container startup      
  3.       //进行logger,manager,cluster,realm,resource的启动      
  4.        super .start();   
  5. }   
public void start() throws LifecycleException { 
       // Standard container startup   
      //进行logger,manager,cluster,realm,resource的启动   
       super.start(); 
} 

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

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. public   synchronized   void  start()  throws  LifecycleException {   
  2. //(省略)  server.xml中配置应用组件的启动       
  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. }   
public synchronized void start() throws LifecycleException { 
//(省略)  server.xml中配置应用组件的启动    
//StandardHost容器的启动,   
        Container children[] = findChildren();   
        for (int i = 0; i < children.length; i++) {   
            if (children[i] instanceof Lifecycle)   
                ((Lifecycle) children[i]).start();   
        }     

    //StandardPipeline的启动(容器与容器间的管道)   
        if (pipeline instanceof Lifecycle)   
            ((Lifecycle) pipeline).start();  
} 

 

(3) StandardHost.start()被调用 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. public   synchronized   void  start()  throws  LifecycleException {   
  2. //返回到以上的containerBase#start执行pipeline      
  3.       super .start();    
  4. }  
public synchronized void start() throws LifecycleException { 
//返回到以上的containerBase#start执行pipeline   
      super.start();  
}

 

(4) StandardPipeline#start

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. public   synchronized   void  start()  throws  LifecycleException {   
  2.        // 将会调用HostConfig#start方法      
  3.        lifecycle.fireLifecycleEvent(START_EVENT, null );     
  4.     
  5.        // Notify our interested LifecycleListeners      
  6.        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null );     
  7. }   
public synchronized void start() throws LifecycleException { 
       // 将会调用HostConfig#start方法   
       lifecycle.fireLifecycleEvent(START_EVENT, null);   
  
       // Notify our interested LifecycleListeners   
       lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);   
} 

 
(5)  HostConfig#start

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. public   void  start() {    
  2.           //部暑webapps      
  3.           deployApps();              
  4.   }   
public void start() {  
          //部暑webapps   
          deployApps();            
  } 

 

(6) HostConfig#deployApps 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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. }  
   protected void deployApps() {   
       File appBase = appBase();   
       File configBase = configBase();   
       // Deploy XML descriptors from configBase   
       deployDescriptors(configBase, configBase.list());   
       // Deploy WARs, and loop if additional descriptors are found   
       deployWARs(appBase, appBase.list());   
       // Deploy expanded folders   
       deployDirectories(appBase, appBase.list());             
   }

  

(7) deployWARs

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. protected   void  deployWARs(File appBase, String[] files) {   
  2. ……   
  3. deployWAR(contextPath, dir, file);            
  4.   }   
protected void deployWARs(File appBase, String[] files) { 
…… 
deployWAR(contextPath, dir, file);          
  } 

 

(8) deployWAR

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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.   }   
protected void deployWAR(String contextPath, File war, String file) { 
if (context instanceof Lifecycle) {   
  // (省略) 
            Class clazz = Class.forName(host.getConfigClass());   
            LifecycleListener listener =   
                (LifecycleListener) clazz.newInstance();   
            ((Lifecycle) context).addLifecycleListener(listener);   
        }   
        context.setPath(contextPath);   
        context.setDocBase(file);   
        //以下这一步跟进去,,StandardContext的启动   
        host.addChild(context);         
  } 

 
(9) StandardContext#start
在Context的启动过程中,主要完成了以下任务。
----------------------------------------------------------------------------------------------------------------------
a) 设置web app的具体目录webappResources。 
b) postWorkDirectory (),创建临时文件目录。Tomcat下面有一个work目录,用来存放临时文件。
c) 触发START_EVENT事件监听,在这个事件监听里面会启动ContextConfig的start()事件,ContextConfig是用来配置web.xml的。
d) 为context创建welcome files,通常是这三个启动文件:index.html、index.htm、index.jsp
e) 配置filter
f) 启动带有<load-on-startup>的Servlet。
g) 注册JMX。
----------------------------------------------------------------------------------------------------------------------
至此,Container启动完毕,下面是connector的启动。

● connector的启动
(1) org.apache.catalina.connector.Connector.start() 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. public   void  start()  throws  LifecycleException {   
  2.            // Http11Protocol的启动    
  3.             protocolHandler.start();   
  4. }  
public void start() throws LifecycleException { 
           // Http11Protocol的启动 
            protocolHandler.start(); 
}

 

(2) Http11Protocol#start 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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.         }  
public void start() throws Exception { 
try {   
            //到了终点的启动   
            endpoint.start();   
        } catch (Exception ex) {   
            log.error(sm.getString("http11protocol.endpoint.starterror"), ex);   
            throw ex;   
        }

  

(3) JIoEndPoint#start 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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.         }  
public void start()   
        throws Exception {            
              
            for (int i = 0; i < acceptorThreadCount; i++) {   
        //这里的acceptor是一个线程,里面是一个serversocket的启动   
                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);   
                acceptorThread.setPriority(threadPriority);   
                acceptorThread.setDaemon(daemon);   
                acceptorThread.start();   
            }   
        }

   

(4) Acceptor#run

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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. }   
public void run() {               
// Accept the next incoming connection from the server socket   
               try {   
          //这里进行了accept(),等待客户端消息,进行接收   
                   Socket socket = serverSocketFactory.acceptSocket(serverSocket);   
                   serverSocketFactory.initSocket(socket);   
                   // Hand this socket off to an appropriate processor   
                   if (!processSocket(socket)) {   
                       // Close socket right away   
                       try {   
                           socket.close();   
                       } catch (IOException e) {   
                           // Ignore   
                       }   
                   }   
               }catch ( IOException x ) {   
                   if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);   
               } catch (Throwable t) {   
                   log.error(sm.getString("endpoint.accept.fail"), t);   
               }   
} 

 

至此Connector.start方法调用完毕。整个server启动完毕。

 

Tomcat源码系列3--Tomcat请求处理的流程

文章分类:Java编程

本次讲解一下Tomcat请求处理的流程,不当之处还请comment。


一. Tomcat 总体结构
Tomcat采用模块化管理,下面是 Tomcat 的总体结构图:

tomcat(一个牛人写的文章,自己看)
 
从上图中可以看出 Tomcat 的核心是两个组件:Connector 和 Container。下面是一些概念的介绍。
① Server
一个server代表了整个catalina servlet容器,在Tomcat里面的Server的用处是启动和监听服务端事件(诸如重启、关闭等命令)。
② Service
Service是由一个或多个Connector与一个Engine的组合。
③ Connector
Connector将在某个指定的端口上监听客户的请求,把从socket传递过来的数据,封装成Request,传递给Engine来处理,并从Engine处获得响应并返回给客户。
Tomcat通常会用到两种Connector:
a) Http Connector 在端口8080处侦听来自客户browser的http请求。
b) AJP Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。

二、请求处理过程解析
1. Connector处理请求
Connector处理请求的流程大致如下:

tomcat(一个牛人写的文章,自己看)
 
Connector组件启动后,会侦听相关的端口的客户端请求。
(1) 接受一个新的连接请求(org.apache.tomcat.util.net.TcpWorkerThread) 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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. }  
void runIt(Object[] perThrData){ 
       Socket s = null; 
            try { 
                s = endpoint.acceptSocket();  //获取一个请求 
            } finally { 
                if (endpoint.isRunning()) { 
                    endpoint.tp.runIt(this); 
  // 此处启动另一个TcpWorkerTread去接受其他请求,此线程处理已接受的请求 
                } 
            }                 
      TcpConnection con = null; 
      con = (TcpConnection) perThrData[0]; 
      con.setEndpoint(endpoint); 
      con.setSocket(s);endpoint.getConnectionHandler().processConnection(con,(Object[]) perThrData[1]);     
}

    
(2) 新接收的请求被传到Http11ConnectionHandler中处理。(org.apache.coyote.http11.Http11Protocol.Http11ConnectionHandler) 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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. }  
void processConnection(TcpConnection connection, Object[] thData){     
    Http11Processor  processor=null; 
    processor=(Http11Processor)thData[Http11Protocol.THREAD_DATA_PROCESSOR];   
    socket=connection.getSocket();                      
    InputStream in = socket.getInputStream();   
    OutputStream out = socket.getOutputStream(); 
    processor.setSocket(socket ); 
    processor.process(in, out);   
//processor是org.apache.coyote.http11.Http11Processor 的 一个实例 
}

 
(3) 在 Http11Processor 中处理 http11 协议相关的信息(org.apache.coyote.http11.Http11Processor)

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  1. void  process(InputStream input, OutputStream output)  throws  IOException{   
  2.         ~~略~~   
  3.         inputBuffer.setInputStream(input);   
  4.         outputBuffer.setOutputStream(output);   
  5.         inputBuffer.parseHeaders();   
  6.       //http11 协议头在此方法中被取出    
  7.         adapter.service(request, response);      
  8.       //adapter 是org.apache.catalina.connector.CoyoteAdapter 的 一个实例    
  9. }   
void process(InputStream input, OutputStream output) throws IOException{ 
        ~~略~~ 
        inputBuffer.setInputStream(input); 
        outputBuffer.setOutputStream(output); 
        inputBuffer.parseHeaders(); 
      //http11 协议头在此方法中被取出 
        adapter.service(request, response);    
      //adapter 是org.apache.catalina.connector.CoyoteAdapter 的 一个实例 
} 

 

接下来的流程交由容器进行处理。
2. 容器处理请求
容器交由Pipeline处理,这个Pipeline里面会放置一些vavle,请求沿着pipeline传递下去并且vavle对其进行相关的处理。比如说日志等,valve还可以自定义,具体需要查看server.xml配置文件。相关类图如下:

tomcat(一个牛人写的文章,自己看)
 
Tomcat 的主要处理组件Engine、Host、Context和Wrapper的实现都会实现Pipeline接口,实际对请求的处理是一个 Adpater,Tomcat中Adapter的实现是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传来的请求调用CoyoteAdapter.service()方法。(org.apache.catalina.connector.CoyoteAdapter) 

Java代码  tomcat(一个牛人写的文章,自己看)tomcat(一个牛人写的文章,自己看)
  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.      //创建request、response对象