Tomcat 源碼分析(-)啓動過程分析

    前面幾篇文章分別介紹了Tomcat的安裝、優化和框架,本文主要用於分析Tomcat源碼啓動過程,研究一個框架最好的着手方式可能就是研究它的啓動過程,由於在這過程當中咱們能夠看到它內部的層次關係,模塊關係等,對整個框架有一個高層次的掌握,後續研究就能夠駕輕就熟了。
    研究Tomcat源碼啓動過程的最簡單方式是能夠經過Tomcat的啓動類Bootstrap着手。java

  1. Tomcat主要類分析
     
    1) Catalina
    Catalina負責Server的加載、啓動和關閉,Bootstrap啓動後將Server的加載、啓動和關閉的操做所有委託到Catalina類。

    2) Server
    Server是Service的生存環境。一個Server中能夠有多個Service。StandardServer是Server的標準實現類,StandardServer能夠持有多個Service。

    2) Service
    Service將Connector和Container包裝在一塊兒提供對外服務,一個Service能夠有多個Connector,可是隻有一個Container。StandardService是Service的標準實現,StandardService分別持有多個Connector和一個Engine,將二者聯繫起來對外工做。

    3) Connector
    Connector負責對外交流。它的主要任務是負責接收瀏覽器的發過來的tcp鏈接請求,建立一個Request和Response對象分別用於和請求端交換數據,而後會產生一個線程來處理這個請求並把產生的Request 和Response對象傳給處理這個請求的線程,處理這個請求的線程就是Container組件要作的事了。
    ProtocolHandler負責選擇底層運行模式Bio或者Nio,監聽端口,建立線程以及數據的處理等,它是對AbstractEndPoint的輕度封裝。Http11NioProtocol表明Nio模式Protocol,Tomcat8之後默認採用此協議。
    AbstractEndPoint表明具體的底層通信,它負責運行模式Bio或者Nio,綁定端口,監聽端口,端口的事件處理等關於Socket的事情。JIoEndPoint表明Bio模式,NioEndPoint表明Nio模式,Tomcat8之後默認都是使用Nio模式。

    4) Engine
    Engine是完整的容器,其下面擁有多個虛擬主機,它的責任就是將Connector請求分配給虛擬機處理。StandardEngine是Engine的標準實現類。

    5) Executor
    Executor工做線程,用於處理Connector傳遞的請求。
     
  2. Tomcat啓動過程
    1) Bootstrap啓動程序,Bootstrap是Tomcat的入口,負責加載和啓動Server
    public static void main(String args[]) {
        if (daemon == null) {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else {
            // When running as a service the call to stop will be on a new
            // thread so make sure the correct class loader is used to prevent
            // a range of class not found exceptions.
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
    
        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }
    
            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                // bootstrap加載Server
                daemon.load(args);
                // bootstrap啓動Server
                daemon.start();
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }
    }

    2) Bootstrap類load方法加載Server,Bootstrap加載Server最終委託給Catalina的load方法
    private void load(String[] arguments) throws Exception {
    
        // Call the load() method
        String methodName = "load";
        Object param[];
        Class<?> paramTypes[];
        if (arguments == null || arguments.length == 0) {
            paramTypes = null;
            param = null;
        } else {
            paramTypes = new Class[1];
            paramTypes[0] = arguments.getClass();
            param = new Object[1];
            param[0] = arguments;
        }
        // Catalina load方法調用
        Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isDebugEnabled())
            log.debug("Calling startup class " + method);
        method.invoke(catalinaDaemon, param);
    }

    3) Catalina load方法負責加載Server
    public void load() {
        long t1 = System.nanoTime();
    
        initDirs();
        initNaming();
    
        // Create and execute our Digester
        Digester digester = createStartDigester();
    
        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            try {
                file = configFile();
                inputStream = new FileInputStream(file);
                inputSource = new InputSource(file.toURI().toURL().toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail", file), e);
                }
            }
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());
                    inputSource = new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail", getConfigFile()), e);
                    }
                }
            }
    
            // This should be included in catalina.jar
            // Alternative: don't bother with xml, just create it manually.
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader().getResourceAsStream("server-embed.xml");
                    inputSource = new InputSource(getClass().getClassLoader().getResource("server-embed.xml").toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail", "server-embed.xml"), e);
                    }
                }
            }
    
            if (inputStream == null || inputSource == null) {
                if  (file == null) {
                    log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml]"));
                } else {
                    log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()));
                    if (file.exists() && !file.canRead()) {
                        log.warn("Permissions incorrect, read permission is not allowed on the file.");
                    }
                }
                return;
            }
    
            try {
                inputSource.setByteStream(inputStream);
                digester.push(this);
                digester.parse(inputSource);
            } catch (SAXParseException spe) {
                log.warn("Catalina.start using " + getConfigFile() + ": " +
                        spe.getMessage());
                return;
            } catch (Exception e) {
                log.warn("Catalina.start using " + getConfigFile() + ": " , e);
                return;
            }
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    
        // 設置Server屬性
        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    
        // Stream redirection
        initStreams();
    
        // Start the new server
        try {
            // Server初始化
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error("Catalina.start", e);
            }
        }
    
        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
        }
    }

    4) StandardServer初始化,StandardServer對應的多個Service也會被一一初始化
    protected void initInternal() throws LifecycleException {
        super.initInternal();
    
        // Register global String cache
        // Note although the cache is global, if there are multiple Servers
        // present in the JVM (may happen when embedding) then the same cache
        // will be registered under multiple names
        onameStringCache = register(new StringCache(), "type=StringCache");
    
        // Register the MBeanFactory
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        onameMBeanFactory = register(factory, "type=MBeanFactory");
    
        // Register the naming resources
        globalNamingResources.init();
    
        // Populate the extension validator with JARs from common and shared
        // class loaders
        if (getCatalina() != null) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            // Walk the class loader hierarchy. Stop at the system class loader.
            // This will add the shared (if present) and common class loaders
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File (url.toURI());
                                if (f.isFile() && f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException e) {
                                // Ignore
                            } catch (IOException e) {
                                // Ignore
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // 初始化全部的service
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }

    5) StandardService初始化,StandardService分別初始化Engine, Executor和Connector
    protected void initInternal() throws LifecycleException {
        super.initInternal();
    
        // 初始化Engine
        if (engine != null) {
            engine.init();
        }
    
        // 初始化全部的Executor
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }
    
        // Initialize mapper listener
        mapperListener.init();
    
        // 初始化全部的Connector
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                try {
                    connector.init();
                } catch (Exception e) {
                    String message = sm.getString("standardService.connector.initFailed", connector);
                    log.error(message, e);
    
                    if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                        throw new LifecycleException(message);
                }
            }
        }
    }

    6) Engine初始化
    protected void initInternal() throws LifecycleException {
        // Ensure that a Realm is present before any attempt is made to start
        // one. This will create the default NullRealm if necessary.
        getRealm();
        super.initInternal();
    }

    7) Connector初始化
    protected void initInternal() throws LifecycleException {
    
        super.initInternal();
    
        // Initialize adapter
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);
    
        // Make sure parseBodyMethodsSet has a default
        if( null == parseBodyMethodsSet ) {
            setParseBodyMethods(getParseBodyMethods());
        }
    
        if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr", getProtocolHandlerClassName()));
        }
        if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() && protocolHandler instanceof AbstractHttp11JsseProtocol) {
            AbstractHttp11JsseProtocol<?> jsseProtocolHandler = (AbstractHttp11JsseProtocol<?>) protocolHandler;
            if (jsseProtocolHandler.isSSLEnabled() && jsseProtocolHandler.getSslImplementationName() == null) {
                // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
                jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
            }
        }
    
        try {
            // 初始化Protocol,Bio或者Nio,開啓監聽端口
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }
    }

    8) Bootstrap start方法啓動Server,Bootstrap啓動Server最終也委託給Catalina的start方法
    public void start() throws Exception {
        if (catalinaDaemon == null) init();
    
        // Cataline start方法調用
        Method method = catalinaDaemon.getClass().getMethod("start", (Class[]) null);
        method.invoke(catalinaDaemon, (Object[]) null);
    
    }

    9) Catalina start方法啓動Server
    public void start() {
    
        if (getServer() == null) {
            load();
        }
    
        if (getServer() == null) {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }
    
        long t1 = System.nanoTime();
    
        // 啓動Server
        try {
            getServer().start();
        } catch (LifecycleException e) {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }
    
        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
        }
    
        // Register shutdown hook
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);
    
            // If JULI is being used, disable JULI's shutdown hook since
            // shutdown hooks run in parallel and log messages may be lost
            // if JULI's hook completes before the CatalinaShutdownHook()
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false);
            }
        }
    
        if (await) {
            await();
            stop();
        }
    }

    10) StandardServer啓動
    protected void startInternal() throws LifecycleException {
    
        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);
    
        globalNamingResources.start();
    
        // 啓動全部的Service
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();
            }
        }
    }

    11) StandardService啓動
    protected void startInternal() throws LifecycleException {
        if(log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name", this.name));
        setState(LifecycleState.STARTING);
    
        // 啓動Engine
        if (engine != null) {
            synchronized (engine) {
                engine.start();
            }
        }
    
        // 啓動全部的Executor
        synchronized (executors) {
            for (Executor executor: executors) {
                executor.start();
            }
        }
    
        mapperListener.start();
    
        // 啓動全部的Connector
        synchronized (connectorsLock) {
            for (Connector connector: connectors) {
                try {
                    // If it has already failed, don't try and start it
                    if (connector.getState() != LifecycleState.FAILED) {
                        connector.start();
                    }
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
        }
    }

    12) StandardEngine啓動
    protected synchronized void startInternal() throws LifecycleException {
    
        // Log our server identification information
        if(log.isInfoEnabled())
            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
    
        // Standard container startup
        super.startInternal();
    }

    13) Connector啓動
    protected void startInternal() throws LifecycleException {
    
        // Validate settings before starting
        if (getPort() < 0) {
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
        }
    
        setState(LifecycleState.STARTING);
    
        try {
            // 啓動Protocol, Bio或者Nio,開始接受請求
            protocolHandler.start();
        } catch (Exception e) {
            String errPrefix = "";
            if(this.service != null) {
                errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
            }
    
            throw new LifecycleException
                (errPrefix + " " + sm.getString
                 ("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }

     
  3. 總結 從Tomcat啓動過程來看,Bootstrap是Tomcat的啓動入口,而Tomcat只是對Catalina的調用,全部的加載、啓動和關閉操做最終都由Catalina完成,Catalina負責控制Server的啓動、加載和關閉,Server的加載、啓動和關閉內部都是依次對Engine, Executor和Connector的調用。經過Tomcat啓動過程,咱們能夠清楚地瞭解Tomcat內部架構,後面能夠更好地瞭解Tomcat內部機制。
相關文章
相關標籤/搜索