Tomcat的源码研读怎么说都是一件恐怖的事情,代码太多!不过管中窥豹,也可偶尔为之。谁让我好读书,不求甚解呢。前面对嵌入式Tomcat(Tomcat 4.0)进行了一个简单的分析,见 在应用中使用嵌入式Tomcat。

今天的这篇文章对Tomcat的源码进行了一些分析,主要是Tomcat启动。

1、Bootstrap.java。正如OFBiz的启动程序时Start.java,Tomcat的启动程序是Bootstrap.java。OFBiz的Start.java做的事情最主要的就是加载相应的lib库和启动tomcat及其component。Tomcat的Bootstrap也是加载相应的lib库和启动Catalina
的process方法。Lib库放置在common,server和share目录中。

2、Catalina.java。这个类主要研究start方法,核心代码如下:

 1   Digester digester  =  createStartDigester();
 2          File file  =  configFile();
 3           try  {
 4              InputSource is  =
 5                   new  InputSource( " file:// "   +  file.getAbsolutePath());
 6              FileInputStream fis  =   new  FileInputStream(file);
 7              is.setByteStream(fis);
 8              digester.push( this );
 9              digester.parse(is);
10              fis.close();
11          }  catch  (Exception e) {
12              System.out.println( " Catalina.start using  "
13                                  +  configFile()  +   " "   +  e);
14              e.printStackTrace(System.out);
15              System.exit( 1 );
16          }

主要是解析server.xml文件,采用的 Digester,非常著名的xml解析器。通过解析的源代码分析,Tomcat将Engine,Host和Context当成Container,这是一个虚拟的概念,具体的容器都实现了Container, Lifecycle接口。所以Service不是直接引用一个Engine,而是一个Container。 在应用中使用嵌入式Tomcat的tomcat实现结构图中都是相邻层次之间都是双向引用的,如Service引用上面的Server还有下级的Connector和Container。大致类图如下:


Digester解析server.xml就是将这些实例初始化并且配置好相互的引用关系。Connector通过Container的invoke方法将接受到了请求交给Container最高层Engine处理:

CoyoteAdapter.java

connector.getContainer().invoke(request, response);


接下来就是初始化并启动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的初始化最终就是将connector进行初始化:

CoyoteConnector.java

    
/**
     * Initialize this connector (create ServerSocket here!)
     
*/
    
public   void  initialize()
        
throws  LifecycleException {

        
if  (initialized)
            
throw   new  LifecycleException
                (sm.getString(
" coyoteConnector.alreadyInitialized " ));

        
this .initialized  =   true ;

        
if ( oname  ==   null   &&  (container  instanceof  StandardEngine)) {
            
try  {
                
//  we are loaded directly, via API - and no name was given to us
                StandardEngine cb = (StandardEngine)container;
                oname 
=  createObjectName(cb.getName(),  " Connector " );
                Registry.getRegistry(
null null )
                    .registerComponent(
this , oname,  null );
            } 
catch  (Exception e) {
                log (
" Error registering connector  "   +  e.toString());
            }
            
if (debug  >   0 )
                log(
" Creating name for connector  "   +  oname);
        }

        
//  Initialize adapter
        adapter  =   new  CoyoteAdapter( this );

        protocolHandler.setAdapter(adapter);

        IntrospectionUtils.setProperty(protocolHandler, 
" jkHome " ,
                                       System.getProperty(
" catalina.base " ));

        
//  Configure secure socket factory
         if  (factory  instanceof  CoyoteServerSocketFactory) {
            IntrospectionUtils.setProperty(protocolHandler, 
" secure " ,
                                           
""   +   true );
            CoyoteServerSocketFactory ssf 
=
                (CoyoteServerSocketFactory) factory;
            IntrospectionUtils.setProperty(protocolHandler, 
" algorithm " ,
                                           ssf.getAlgorithm());
            IntrospectionUtils.setProperty(protocolHandler, 
" ciphers " ,
                                           ssf.getCiphers());
            IntrospectionUtils.setProperty(protocolHandler, 
" clientauth " ,
                                           ssf.getClientAuth());
            IntrospectionUtils.setProperty(protocolHandler, 
" keystore " ,
                                           ssf.getKeystoreFile());
            IntrospectionUtils.setProperty(protocolHandler, 
" randomfile " ,
                                           ssf.getRandomFile());
            IntrospectionUtils.setProperty(protocolHandler, 
" rootfile " ,
                                           ssf.getRootFile());

            IntrospectionUtils.setProperty(protocolHandler, 
" keypass " ,
                                           ssf.getKeystorePass());
            IntrospectionUtils.setProperty(protocolHandler, 
" keytype " ,
                                           ssf.getKeystoreType());
            IntrospectionUtils.setProperty(protocolHandler, 
" protocol " ,
                                           ssf.getProtocol());
            IntrospectionUtils.setProperty(protocolHandler,
                                           
" sSLImplementation " ,
                                           ssf.getSSLImplementation());
        }

        
try  {
            protocolHandler.init();
        } 
catch  (Exception e) {
            
throw   new  LifecycleException
                (sm.getString
                 (
" coyoteConnector.protocolHandlerInitializationFailed " , e));
        }
    }

Protocol Handler初始化会初始化PoolTcpEndPoint,这个时候就会启动Tomcat的端口了:

     public   void  initEndpoint()  throws  IOException, InstantiationException {
        
try  {
            
if (factory == null )
                factory
= ServerSocketFactory.getDefault();
            
if (serverSocket == null ) {
                
try  {
                    
if  (inet  ==   null ) {
                        serverSocket 
=  factory.createSocket(port, backlog);
                    } 
else  {
                        serverSocket 
=  factory.createSocket(port, backlog, inet);
                    }
                } 
catch  ( BindException be ) {
                    
throw   new  BindException(be.getMessage()  +   " : "   +  port);
                }
            }
            
if ( serverTimeout  >=   0  )
                serverSocket.setSoTimeout( serverTimeout );
        } 
catch ( IOException ex ) {
            
throw  ex;
        } 
catch ( InstantiationException ex1 ) {
            
throw  ex1;
        }
        initialized 
=   true ;
    }

初始化后整个tomcat就做好准备启动,这时还不能处理客户端的请求,必须启动相关容器。与初始化相比,启动的时候同时启动Container和Connector。Container的启动会将Engine,Host和Conext都启动起来。

Connector启动的时候就会启动PoolTcpEndPoint,看看它的run方法就大概知道怎么回事了:

     /**
     * The background thread that listens for incoming TCP/IP connections and
     * hands them off to an appropriate processor.
     
*/
    
public   void  run() {

        
//  Loop until we receive a shutdown command
         while  (running) {

            
//  Loop if endpoint is paused
             while  (paused) {
                
try  {
                    Thread.sleep(
1000 );
                } 
catch  (InterruptedException e) {
                    
//  Ignore
                }
            }

            
//  Allocate a new worker thread
            MasterSlaveWorkerThread workerThread  =  createWorkerThread();
            
if  (workerThread  ==   null ) {
                
try  {
                    
//  Wait a little for load to go down: as a result, 
                    
//  no accept will be made until the concurrency is
                    
//  lower than the specified maxThreads, and current
                    
//  connections will wait for a little bit instead of
                    
//  failing right away.
                    Thread.sleep( 100 );
                } 
catch  (InterruptedException e) {
                    
//  Ignore
                }
                
continue ;
            }
            
            
//  Accept the next incoming connection from the server socket
            Socket socket  =  acceptSocket();

            
//  Hand this socket off to an appropriate processor
            workerThread.assign(socket);

            
//  The processor will recycle itself when it finishes

        }

        
//  Notify the threadStop() method that we have shut ourselves down
         synchronized  (threadSync) {
            threadSync.notifyAll();
        }

    }

MasterSlaveWorkerThread默认最大线程数是20,Tomcat优化时可以设置此线程数,见 Tomcat优化方案
MasterSlaveWorkerThread使用的是Guarded Suspension Pattern,如果有新的Socket分配,那么进行处理,available作为警戒条件:

     synchronized   void  assign(Socket socket) {

        
//  Wait for the Processor to get the previous Socket
         while  (available) {
            
try  {
                wait();
            } 
catch  (InterruptedException e) {
            }
        }

        
//  Store the newly available Socket and notify our thread
         this .socket  =  socket;
        available 
=   true ;
        notifyAll();

    }

    
    
/**
     * Await a newly assigned Socket from our Connector, or <code>null</code>
     * if we are supposed to shut down.
     
*/
    
private   synchronized  Socket await() {

        
//  Wait for the Connector to provide a new Socket
         while  ( ! available) {
            
try  {
                wait();
            } 
catch  (InterruptedException e) {
            }
        }

        
//  Notify the Connector that we have received this Socket
        Socket socket  =   this .socket;
        available 
=   false ;
        notifyAll();

        
return  (socket);

    }

    public void run() {

        // Process requests until we receive a shutdown signal
        while (!stopped) {

            // Wait for the next socket to be assigned
            Socket socket = await();
            if (socket == null)
                continue;

            // Process the request from this socket
            endpoint.processSocket(socket, con, threadData);

            // Finish up this request
            endpoint.recycleWorkerThread(this);

        }

        // Tell threadStop() we have shut ourselves down successfully
        synchronized (threadSync) {
            threadSync.notifyAll();
        }

    }



下面一幅图有助理解,Tomcat请求客户端情况的具体分析后面再分析。