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(
nullnull)
                    .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請求客戶端情況的具體分析后面再分析。