艾辉 发表于 2017-1-27 11:50:37

【转】Tomcat启动源代码分析

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方法,核心代码如下:



1Digester 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请求客户端情况的具体分析后面再分析。



http://www.blogjava.net/persister/archive/2009/06/17/282875.html
页: [1]
查看完整版本: 【转】Tomcat启动源代码分析