九.吊打面试官系列-Tomcat优化-深入源码剖析Tomcat启动流程

前言

上篇文章《Tomcat优化-Tomcat如何打破双亲委派》我们深入分析了一下JVM的ClassLoader类加载器机制,以及Tomcat通过打破双亲委派来实现自己的加载隔离,本篇文章接着上篇文章分析Tomcat的启动流程,主要是看Tomcat是如何进行初始化的。如果还没看前面2章的请先看,不然会有点吃力哦,特别是《深入Tomcat底层架构

回顾

先来简单回顾一下Tomcat中的几个核心组件,看下图

  1. Tomcat最外层的是Server,一个Server代表一个服务器
  2. 一个Server中包含多个Service,这样可以实现通过不同的端口号来访问同一台机器上部署的不同应用
  3. Connector : 监听一个端口,Connector用于接收请求并将接收的请求封装为Request和Response再交由Engine进行处理,处理完请求之后再返回给Connector,最后在由Connector通过Socket将处理的结果返回给客户端
  4. Container : 用于封装和管理Servlet,和处理Request请求;Tomcat 设计了 4 种容器,分别是 Engine、Host、Context 和 Wrapper。这 4 种容器不是平行关系,而是父子关系。
  • Engine:引擎,Servlet 的顶层容器,用来管理多个虚拟站点,一个 Service 最多只能有一个 Engine;
  • Host:虚拟主机,负责 web 应用的部署和 Context 的创建。可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可以部署多个 Web 应用程序;可以给每个Host配置一个域名
  • Context:Web 应用上下文,包含多个 Wrapper,负责 web 配置的解析、管理所有的 Web 资源。一个Context对应一个 Web 应用程序。
  • Wrapper:表示一个 Servlet,最底层的容器,是对 Servlet 的封装,负责 Servlet 实例的创建、执行和销毁

在这里插入图片描述

Tomcat启动流程

BootStrap#main Tomcat入口方法

Tomcat的启动类是在 org.apache.catalina.startup.Bootstrap#main中,通过执行main方法来启动,该方法中会创建一个Bootstrap对象,然后执行Bootstrap.init()方法来进行初始化。同时该方法中维护了 Bootstrap 的 start ,stop等生命周期方法的入口,源码如下

public static void main(String args[]) {

        synchronized (daemonLock) {
            if (daemon == null) {
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try {
                	//1.初始化Tomcat
                    bootstrap.init();
                } catch (Throwable t) {
                    handleThrowable(t);
                    log.error("Init exception", t);
                    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];
            }
			//触发startd指令
            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
               //触发 stop执行
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } 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);
            log.error("Error running command", t);
            System.exit(1);
        }
    }

bootstrap#init Tomcat 初始化

下面我们切入到bootstrap#init初始化方法中,该方法中会调用 initClassLoaders初始化Tomcat自定义的类加载器,下面我们可以看到三个类加载器分别是:commonLoader,catalinaLoader,sharedLoader。三个类加载器创建好之后,会通过catalinaLoader加载 Catalina.class并实例化它。并把sharedLoader作为Catalina的setParentClassLoader父类加载器。如下:


//Tomcat中自定义的classLoader
ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;

//初始化ClassLoader
private void initClassLoaders() {
     try {
         commonLoader = createClassLoader("common", null);
         if (commonLoader == null) {
             // no config file, default to this loader - we might be in a 'single' env.
             commonLoader = this.getClass().getClassLoader();
         }
         catalinaLoader = createClassLoader("server", commonLoader);
         sharedLoader = createClassLoader("shared", commonLoader);
     } catch (Throwable t) {
         handleThrowable(t);
         log.error("Class loader creation threw exception", t);
         System.exit(1);
     }
 }
 
//初始化bootstrap
public void init() throws Exception {
		//初始化类加载器
        initClassLoaders();
		
        Thread.currentThread().setContextClassLoader(catalinaLoader);
	
        SecurityClassLoad.securityClassLoad(catalinaLoader);

        // Load our startup class and call its process() method
        if (log.isTraceEnabled()) {
            log.trace("Loading startup class");
        }
        //加载 Catalina 类
        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        //实例化 Catalina 对象
        Object startupInstance = startupClass.getConstructor().newInstance();

        // Set the shared extensions class loader
        if (log.isTraceEnabled()) {
            log.trace("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;
        //调用 Catalina的setParentClassLoader,为Catalina设置 parent 类加载器
        Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);

        catalinaDaemon = startupInstance;
    }

bootstrap#startd Tomcat启动

初始化好类加载器之后,就会执行Tomcat的生命周期方法,这个会根据启动started ,stoped指令做判断,然后调用bootstrap#startd , bootstrap#stoped 来决定是启动或者停止Tomcat

//启动
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();
   } 

bootstrap#load 加载配置

如果是startd会先调用 bootstrap#load 来加载tomcat的基础配置,而该方法中会通过反射调用 org.apache.catalina.startup.Catalina#load 方法来加载

    private void load(String[] arguments) throws Exception {

        // Call the load() method
        //调用load方法
        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;
        }
        //通过反射调用  org.apache.catalina.startup.Catalina#load方法
        Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isTraceEnabled()) {
            log.trace("Calling startup class " + method);
        }
        method.invoke(catalinaDaemon, param);
    }

bootstrap#load 主要通过反射调用 Catalina#load 去做加载

Catalina#load 加载配置

该方法默认会从conf/server.xml 加载并解析Tomcat的配置,然后调用 StandardServer#init进行初始化

public class Catalina {
	protected static final StringManager sm = StringManager.getManager(Constants.Package);
	//默认加载的配置文件
    public static final String SERVER_XML = "conf/server.xml";
    protected String configFile = SERVER_XML;

	//父类加载器
    protected ClassLoader parentClassLoader = Catalina.class.getClassLoader();
    /**
     * The server component we are starting or stopping.
     */
     //服务对象 :通过Server来停止或者启动服务,实现类是:StandardServer
    protected Server server = null;



  /**
     * Start a new server instance.
     */
    public void load() {

       ...
       //开始解析server.xml配置文件
        // Parse main server.xml
        parseServerXml(true);
        Server s = getServer();
        if (s == null) {
            return;
        }
		//把Catalina对象交给Server
        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();
		//调用Server#init初始化
        // Start the new server
        try {
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new Error(e);
            } else {
                log.error(sm.getString("catalina.initError"), e);
            }
        }

        if (log.isInfoEnabled()) {
            log.info(sm.getString("catalina.init",
                    Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
        }
    }


}

Lifecycle 生命周期接口

Server的唯一实现类是StandardServer,代表着一个Tomcat服务,它
间接实现了Lifecycle接口,Lifecycle是Tomcat提供的生命周期接口,其中包含了对象的start,stop,init,destory等生命周期方法。在这里插入图片描述

下面是org.apache.catalina.Lifecycle接口源码

public interface Lifecycle {
	......
    //销毁
    void destroy() throws LifecycleException;
    void stop() throws LifecycleException;
	//启动
    void start() throws LifecycleException;
	//初始化
    void init() throws LifecycleException;
}

同时Tomcat还为 Lifecycle 提供了一个抽象实现LifecycleBase,它复写了这些生命周期方法,然后通过抽象方法的方式让其子类去具体实现,如下

public abstract class LifecycleBase implements Lifecycle {

	//初始化方法
	    @Override
    public final synchronized void init() throws LifecycleException {
        	...
            initInternal();
            ...
    }
    //抽象方法子类实现

    protected abstract void initInternal() throws LifecycleException;

	//启动方法
	@Override
    public final synchronized void start() throws LifecycleException {

       		...
            startInternal();
          	...
    }
    //抽象方法子类实现
     protected abstract void startInternal() throws LifecycleException;


}

对于init, start, stop,destory 方法都是如此实现,这就是典型的模板方法设计模式 ,除了StandardServer外还有几个很核心的组件也是 Lifecycle 的子类如:StandardService,Connector,StandardEngine,StandardHost,StandardContext,StandardWrapper,都实现了生命周期方法。如下:
在这里插入图片描述
有了生命周期方法之后,Tomcat的各个组件就可以做到一键启停了,非常方便。

组件初始化

StandardServer#init 初始化

代码回到 Catalina#load方法,该方法中会调用 getServer().init(); 对Server进行初始化,其实现类是:StandardServer,而init方法又在父类LifecycleBase类中,

public abstract class LifecycleBase implements Lifecycle {

	public final synchronized void init() throws LifecycleException {
     		...
            initInternal();
			...
    }

	//调用子类的方法
    /**
     * Sub-classes implement this method to perform any instance initialisation
     * required.
     *
     * @throws LifecycleException If the initialisation fails
     */
    protected abstract void initInternal() throws LifecycleException;

}

//子类,最终通过 initInternal 来初始化

public final class StandardServer extends LifecycleMBeanBase implements Server {
	 @Override
    protected void initInternal() throws LifecycleException {
		//先初始化父类
        super.initInternal();

     	...省略...
        // Initialize our defined Services
        //找到当前Server下的所有service,调用Service#init方法初始化
        for (Service service : findServices()) {
            service.init();
        }
    }
}

StandardService#init 初始化

StandardServer#initInternal方法中会找到该Server下的所有Service,然后调用其init方法,Service具体的实现类是 StandardService,它继承了 LifecycleMBeanBase,实现了Service接口。 和 StandardServer 一样,service#init 方法也是在 LifecycleMBeanBase父类中,然后通过 抽象方法 initInternal 来调用子类的实现,所以最终初始化的代码在StandardService#initInternal 方法中

public class StandardService extends LifecycleMBeanBase implements Service {

	...省略...
	
	@Override
    protected void initInternal() throws LifecycleException {
		//父类先初始化
        super.initInternal();

        if (engine != null) {
            engine.init();
        }
		//初始化执行器:线程池
        // Initialize any Executors
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }
		
        // Initialize mapper listener
        mapperListener.init();
		//找到service下的所有 Connector 然后进行初始化
        // Initialize our defined Connectors
        for (Connector connector : findConnectors()) {
            connector.init();
        }
    }
}

Connector#init 连接器初始化

连接器Connector也 继承了 LifecycleBase ,同样的道理,在调用init方法的时候会通过父类的init调用抽象方法:initInternal ,然后通过之类来实现,下面是Connector#initInternal 方法


@Override
    protected void initInternal() throws LifecycleException {
		//父类先初始化
        super.initInternal();
		//protocolHandler 协议处理器,默认实现是NIO : Http11NioProtocol 
        if (protocolHandler == null) {
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
        }

        // Initialize adapter
        //初始化 适配器对象
        adapter = new CoyoteAdapter(this);
        //把适配器设置给Http11NioProtocol
        protocolHandler.setAdapter(adapter);

       ...省略...

        try {
        	//对 protocolHandler 进行初始化
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }
    }

ProtocolHandler.init 初始化

在 Connector#initInternal 方法中会创建 CoyoteAdapter适配器对象,然后调用 ProtocolHandler.init 对ProtocolHandler进行初始化,默认的实现是 Http11NioProtocol(NIO协议)。Http11NioProtocol没有init方法,它是通过其父类 AbstractHttp11Protocol#init来初始化,源码如下

public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {

	 @Override
    public void init() throws Exception {
        // Upgrade protocols have to be configured first since the endpoint
        // init (triggered via super.init() below) uses this list to configure
        // the list of ALPN protocols to advertise
        for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
            configureUpgradeProtocol(upgradeProtocol);
        }
		//通过父类初始化
        super.init();

        // Set the Http11Protocol (i.e. this) for any upgrade protocols once
        // this has completed initialisation as the upgrade protocols may expect this
        // to be initialised when the call is made
        for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
            upgradeProtocol.setHttp11Protocol(this);
        }
    }




public abstract class AbstractProtocol<S> implements ProtocolHandler, MBeanRegistration {

	 @Override
    public void init() throws Exception {
        if (getLog().isInfoEnabled()) {
            getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
            logPortOffset();
        }

        if (oname == null) {
            // Component not pre-registered so register it
            oname = createObjectName();
            if (oname != null) {
                Registry.getRegistry(null, null).registerComponent(this, oname, null);
            }
        }

        if (this.domain != null) {
            ObjectName rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
            this.rgOname = rgOname;
            Registry.getRegistry(null, null).registerComponent(getHandler().getGlobal(), rgOname, null);
        }

        String endpointName = getName();
        endpoint.setName(endpointName.substring(1, endpointName.length() - 1));
        endpoint.setDomain(domain);
		//Endpoint 初始化
        endpoint.init();
    }

}

Endpoint#init 初始化

我们看到 AbstractProtocol#init方法中最终会对 Endpoint进行初始化,Endpoint 默认的实现是 NioEndpoint,它也是通过父类的init来进行初始化的,org.apache.tomcat.util.net.AbstractEndpoint#init

public abstract class AbstractEndpoint<S,U> {

public final void init() throws Exception {
        if (bindOnInit) {
            bindWithCleanup();
            bindState = BindState.BOUND_ON_INIT;
        }
        if (this.domain != null) {
            // Register endpoint (as ThreadPool - historical name)
            oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
            Registry.getRegistry(null, null).registerComponent(this, oname, null);

            ObjectName socketPropertiesOname = new ObjectName(domain +
                    ":type=SocketProperties,name=\"" + getName() + "\"");
            socketProperties.setObjectName(socketPropertiesOname);
            Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

            for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
                registerJmx(sslHostConfig);
            }
        }
    }

}

到这里init的流程就结束了,这里话了一个图总结一下
在这里插入图片描述

组件启动

bootstrap#start 启动

组件的启动和初始化流程差不多,首先回到Bootstrap#mian方法,执行完 load方法后就会执行bootstrap#start方法来启动Tomcat, bootstrap#start 调用的是Catalina#start方法来启动

public void start() throws Exception {
        if (catalinaDaemon == null) {
            init();
        }

        Method method = catalinaDaemon.getClass().getMethod("start", (Class[]) null);
        //调用catalina#start方法
        method.invoke(catalinaDaemon, (Object[]) null);
    }

Catalina#start 启动

下面是 Catalina # start启动方法,方法中会拿到StandardServer执行其start方法,源码如下

public class Catalina {


 /**
     * Start a new server instance.
     */
    public void start() {

        //启动Server,实现类是StandardServer

        // Start the new server
        try {
            getServer().start();
        } catch (LifecycleException e) {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug(sm.getString("catalina.destroyFail"), e1);
            }
            return;
        }

       ...省略...
    }
}


StandardServer#startInternal 启动

StandardServer#Start方法和init方法一样,也是通过org.apache.catalina.util.LifecycleBase#start 生命周期父类来调用,最终通过抽象的startInternal方法来启动Server,下面是 StandardServer#startInternal方法代码

@Override
    protected void startInternal() throws LifecycleException {

       	//找到Server下的所有Service,执行Start方法
        // Start our defined Services
        for (Service service : findServices()) {
            service.start();
        }

        if (periodicEventDelay > 0) {
            monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(this::startPeriodicLifecycleEvent, 0, 60,
                    TimeUnit.SECONDS);
        }
    }

在StandardServer#startInternal方法中会找到所有的Service调用其satrt方法来启动。默认会执行StandardService#start,然后通过父类生命周期类调用org.apache.catalina.core.StandardService#startInternal方法,源码如下

 @Override
    protected void startInternal() throws LifecycleException {

        if (log.isInfoEnabled()) {
            log.info(sm.getString("standardService.start.name", this.name));
        }
        //设置状态:启动中
        setState(LifecycleState.STARTING);

        // Start our defined Container first
        //启动engine容器
        if (engine != null) {
            engine.start();
        }
		//启动线程池
        for (Executor executor : findExecutors()) {
            executor.start();
        }
		//启动映射监听器
        mapperListener.start();

        // Start our defined Connectors second
        //找到拦截器,启动连接器
        for (Connector connector : findConnectors()) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
                connector.start();
            }
        }
    }

StandardService#startInternal 启动

StandardService#startInternal方法中会去找到 Engine 和 Connector去启动。

  • Engine#start方法会继续往后调用子容器,比如:Host,Context,Wrapper
  • Connector#start方法会启动 NIoEndpoint 去监听Socket请求
protected void startInternal() throws LifecycleException {

        if (log.isInfoEnabled()) {
            log.info(sm.getString("standardService.start.name", this.name));
        }
        setState(LifecycleState.STARTING);

        // Start our defined Container first
        //【重要】启动 Engine
        if (engine != null) {
            engine.start();
        }

        for (Executor executor : findExecutors()) {
            executor.start();
        }

        mapperListener.start();

        // Start our defined Connectors second
        for (Connector connector : findConnectors()) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
            	//【重要】连接器的启动
                connector.start();
            }
        }
    }

先说Engine,它默认的实现是StandardEngine,它的startInternal方法直接调用了父类ContainerBase 基础容器类的 startInternal来启动,org.apache.catalina.core.StandardEngine#startInternal源码如下

    @Override
    protected void startInternal() throws LifecycleException {

        // Log our server identification information
        if (log.isInfoEnabled()) {
            log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
        }
		//调用父类的启动方法
        // Standard container startup
        super.startInternal();
    }

ContainerBase#startInternal 通过父类启动

下面是其父类 org.apache.catalina.core.ContainerBase#startInternal 的源码,方法中会拿到所有的子容器,然后包装到 StartChild 中去执行。StartChild是一个Callable线程。然后交给一个线程池去依次执行

protected void startInternal() throws LifecycleException {

        reconfigureStartStopExecutor(getStartStopThreads());

        // Start our subordinate components, if any
        logger = null;
        getLogger();
        Cluster cluster = getClusterInternal();
        if (cluster instanceof Lifecycle) {
            ((Lifecycle) cluster).start();
        }
        Realm realm = getRealmInternal();
        if (realm instanceof Lifecycle) {
            ((Lifecycle) realm).start();
        }

        // Start our child containers, if any
        //拿到所有的子容器,比如:StandardEndpoint的子容器是StandardHost
        Container[] children = findChildren();
        List<Future<Void>> results = new ArrayList<>(children.length);
        for (Container child : children) {
        	//【重要】把子容器交给线程池去执行,StartChild是线程对象
            results.add(startStopExecutor.submit(new StartChild(child)));
        }

        MultiThrowable multiThrowable = null;

        for (Future<Void> result : results) {
            try {
                result.get();
            } catch (Throwable e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                if (multiThrowable == null) {
                    multiThrowable = new MultiThrowable();
                }
                multiThrowable.add(e);
            }

        }
        if (multiThrowable != null) {
            throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
                    multiThrowable.getThrowable());
        }

        // Start the Valves in our pipeline (including the basic), if any
        //【重要】责任链
        if (pipeline instanceof Lifecycle) {
            ((Lifecycle) pipeline).start();
        }

        setState(LifecycleState.STARTING);

        // Start our thread
        //【重要】让线程跑起来,后台去执行 容器
        if (backgroundProcessorDelay > 0) {
            monitorFuture = Container.getService(ContainerBase.this).getServer().getUtilityExecutor()
                    .scheduleWithFixedDelay(new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
        }
    }
ContainerBackgroundProcessor 后台任务

在该方法中容器对象 Container(Engine,Host,Context,Wrapper) 被交给一个 StartChild 线程对象,然后通过线程池去执行 Container#start方法,除此之外Tomcat还通过线程池启动了一个后台任务:scheduleWithFixedDelay(new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS); ContainerBackgroundProcessorMonitor 也是一个runnable 它通过 threadStart 方法来调用 ContainerBackgroundProcessor

 protected void threadStart() {
       ...
            backgroundProcessorFuture = Container.getService(this).getServer().getUtilityExecutor()
                    .scheduleWithFixedDelay(new ContainerBackgroundProcessor(), backgroundProcessorDelay,
                            backgroundProcessorDelay, TimeUnit.SECONDS);
        }
    }

ContainerBackgroundProcessor 也是一个runnable,它的run方法中会调用一个 processChildren方法,该方法会调用此容器及其子容器的 backgroundProcess 方法

protected class ContainerBackgroundProcessor implements Runnable {

        @Override
        public void run() {
            processChildren(ContainerBase.this);
        }

        protected void processChildren(Container container) {
            ClassLoader originalClassLoader = null;

            try {
                if (container instanceof Context) {
                    Loader loader = ((Context) container).getLoader();
                    // Loader will be null for FailedContext instances
                    if (loader == null) {
                        return;
                    }

                    // Ensure background processing for Contexts and Wrappers
                    // is performed under the web app's class loader
                    originalClassLoader = ((Context) container).bind(false, null);
                }
                //【重要】调用容器的 backgroundProcess 方法
                container.backgroundProcess();
                Container[] children = container.findChildren();
                for (Container child : children) {
                    if (child.getBackgroundProcessorDelay() <= 0) {
                    //[重要]调用子容器的 backgroundProcess
                        processChildren(child);
                    }
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("containerBase.backgroundProcess.error"), t);
            } finally {
                if (container instanceof Context) {
                    ((Context) container).unbind(false, originalClassLoader);
                }
            }
        }
    }
container.backgroundProcess 后台任务

container.backgroundProcess 该方法最终会通过父类ContainerBase#backgroundProcess 方法来执行,而在该方法中会发布一个事件

public synchronized void backgroundProcess() {

        ...
        fireLifecycleEvent(PERIODIC_EVENT, null);
    }

    protected void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(this, type, data);
        for (LifecycleListener listener : lifecycleListeners) {
            listener.lifecycleEvent(event);
        }
    }

发布事件会触发LifecycleListener#lifecycleEvent 方法,其中比较重要的一个实现是org.apache.catalina.startup.HostConfig#lifecycleEvent ,它里面包含了对项目的部署逻辑

public void lifecycleEvent(LifecycleEvent event) {

        // Identify the host we are associated with
        try {
            host = (Host) event.getLifecycle();
            if (host instanceof StandardHost) {
                setCopyXML(((StandardHost) host).isCopyXML());
                setDeployXML(((StandardHost) host).isDeployXML());
                setUnpackWARs(((StandardHost) host).isUnpackWARs());
                setContextClass(((StandardHost) host).getContextClass());
            }
        } catch (ClassCastException e) {
            log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
            return;
        }

        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
            check();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.START_EVENT)) {
            start();
        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            stop();
        }
    }



 protected void check() {

     	... 发布项目
            // Hotdeploy applications
            deployApps();
        }
    }
HostConfig#deployApps 发布项目

在 HostConfig#lifecycleEvent 方法中会调用check做一些列检查,然后代用 deployApps()方法去发布项目,各种部署方式都有,如下:

    protected void deployApps() {
    	// catalina-home/webapps 目录
        File appBase = host.getAppBaseFile();
        //拿到Tomcat默认的项目目录
        File configBase = host.getConfigBaseFile();
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        // Deploy XML descriptors from configBase
        //从xml文件中去发布项目
        deployDescriptors(configBase, configBase.list());
        // Deploy WARs
        //发布war包
        deployWARs(appBase, filteredAppPaths);
        // Deploy expanded folders
        //发布外部项目
        deployDirectories(appBase, filteredAppPaths);
    }

StandardHost#start 启动

StandardHost是在ContainerBase#startInternal中,通过 startStopExecutor.submit(new StartChild(child)) ,去触发调用的,
这里设计很巧妙,比如:当前执行的容器是StandardEngine#startInternal,在方法中拿到的子容器findChildren 是StandardHost容器,把 StandardHost 包装到StartChild线程对象去执行,StartChild#call方法中会调用子容器的 start方法

 private static class StartChild implements Callable<Void> {
		//子容器
        private Container child;

        StartChild(Container child) {
            this.child = child;
        }

        @Override
        public Void call() throws LifecycleException {
        	//启动子容器 :Engine -> Host -> Context -> wrapper
            child.start();
            return null;
        }
    }

这里我把始终容器的继承关系和start调用关系画了一个图方便理解如下:

  • 要注意的是这几个容器都是通过 ContainerBase#startInternal 来完成Start调用的
    在这里插入图片描述

下面是 StandardHost 的源码 org.apache.catalina.core.StandardHost#startInternal,这里很有意思,他加入了2个东西

  • Pipeline :管道
  • Valve :阀门 ,这个在 Tomcat第一章《Tomcat核心架构》有讲过Pipeline-Valve 责任链,Tomcat通过Pipeline+Valve 责任链完成容器的调用
protected void startInternal() throws LifecycleException {

        // Set error report valve
        String errorValve = getErrorReportValveClass();
        if ((errorValve != null) && (!errorValve.equals(""))) {
            try {
                boolean found = false;
                Valve[] valves = getPipeline().getValves();
                for (Valve valve : valves) {
                    if (errorValve.equals(valve.getClass().getName())) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    Valve valve = ErrorReportValve.class.getName().equals(errorValve) ? new ErrorReportValve() :
                            (Valve) Class.forName(errorValve).getConstructor().newInstance();
                    getPipeline().addValve(valve);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("standardHost.invalidErrorReportValveClass", errorValve), t);
            }
        }
        //调用父类的启动方法
        super.startInternal();
    }

然而再其父类 org.apache.catalina.core.ContainerBase#startInternal 方法中,提供了pipeline的调用方法((Lifecycle) pipeline).start()

protected void startInternal() throws LifecycleException {

        reconfigureStartStopExecutor(getStartStopThreads());

        // Start our subordinate components, if any
        logger = null;
        getLogger();
        //是否是集群部署
        Cluster cluster = getClusterInternal();
        if (cluster instanceof Lifecycle) {
            ((Lifecycle) cluster).start();
        }
        Realm realm = getRealmInternal();
        if (realm instanceof Lifecycle) {
            ((Lifecycle) realm).start();
        }

        // Start our child containers, if any
        Container[] children = findChildren();
        List<Future<Void>> results = new ArrayList<>(children.length);
        for (Container child : children) {
        	//调用子容器
            results.add(startStopExecutor.submit(new StartChild(child)));
        }
		...
        //启动我们管道中的阀门(包括基本阀门)(如果有)
        // Start the Valves in our pipeline (including the basic), if any
        if (pipeline instanceof Lifecycle) {
            ((Lifecycle) pipeline).start();
        }
		...
		//这里通过线程池开启后台线程
       	if (backgroundProcessorDelay > 0) {
            monitorFuture = Container.getService(ContainerBase.this).getServer().getUtilityExecutor()
                    .scheduleWithFixedDelay(new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
        }
    }

该方法中还会启动后台线程的执行,最终会调用 org.apache.catalina.core.ContainerBase#threadStart 开启后台线程处理,比如:定时检查SESSION超时。

pipeline#start 责任链

我们看到在 ContainerBase#startInternal 方法中还加入了 pipeline的调用,这里采用的是责任链模式。下面我把Tomcat的启动流程画了一个图尝试理解一下。
在这里插入图片描述

Connector#start 启动

连接器的启动是在 org.apache.catalina.core.StandardService#startInternal方法中,找到所有的Connector 调用 start来启动,最终调用Connector的startInternal方法

protected void startInternal() throws LifecycleException {

        // Validate settings before starting
        String id = (protocolHandler != null) ? protocolHandler.getId() : null;
        if (id == null && getPortWithOffset() < 0) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
        }

        setState(LifecycleState.STARTING);

        // Configure the utility executor before starting the protocol handler
        if (protocolHandler != null && service != null) {
            protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
        }

        try {
            protocolHandler.start();
        } catch (Exception e) {
            // Includes NPE - protocolHandler will be null for invalid protocol if throwOnFailure is false
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }

ProtocolHandler#start 启动

在该方法中会先为ProtocolHandler(默认是:Http11NioProtocol )指定Executor执行器,然后执行其 start方法,而在ProtocolHandler#start方法中又会去启动endpoint#start,下面是:org.apache.coyote.AbstractProtocol#start源码

public void start() throws Exception {
        if (getLog().isInfoEnabled()) {
            getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
            logPortOffset();
        }
		//启动 endpoint端点
        endpoint.start();
        //开启超时检查
        monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(() -> {
            startAsyncTimeout();
        }, 0, 60, TimeUnit.SECONDS);
    }

Endpoint#Starter 启动

Endpoint是用来监听请求的,在NIoEndpoint#startInternal方法中会启动 NIO 端点,创建接受器Accpetor、Poller轮询器线程等,org.apache.tomcat.util.net.NioEndpoint#startInternal源码如下

public void startInternal() throws Exception {

        if (!running) {
            running = true;
            paused = false;
			...

            // Create worker collection
            //创建处理work线程的线程池
            if (getExecutor() == null) {
                createExecutor();
            }
			//初始化请求连接数量,通过LimitLatch来现在最大连接数
            initializeConnectionLatch();

            // Start poller thread
            //开启 poller 线程
            poller = new Poller();
            Thread pollerThread = new Thread(poller, getName() + "-Poller");
            pollerThread.setPriority(threadPriority);
            pollerThread.setDaemon(true);
            pollerThread.start();
			//启动accptor线程
            startAcceptorThread();
        }
    }

在NioEndpoint中维护了一个 Acceptor , Acceptor本身是一个线程对象,他通过NIO事件轮询机制去接受Socket请求。如果你看过 《深入Tomcat核心架构》就能理解这,如果没看过不妨去看看。到这里代表Tomcat启动成功

在这里插入图片描述

Tomcat启动总结

  1. Tomcat的启动流程入口是 Bootsrap#main方法,他会先执行 init 进行初始化类加载器和实例化Catalina对象。它通过自定义WebAppClassloader类加载器来实现不同APP应用的加载隔离,从而打破了传统JVM的双亲委派机制
  2. 初始化完成后通过反射调用Catalina#load方法,该方法会通过Lifecycle生命周期接口来实现一键调用相关组件的init 和 start方法,比如:StandardServer ,StandardService,StandardEndpoint ,StandardHost…都是 Lifecycle的之类 , 这里用到模板方法设计模式
  3. Lifecycle提供了一个抽象类:LifecycleBase ,所有的容器对象都通过 LifecycleBase 来实现一一调用。比如:Server的初始化,Service的初始化,Connector等
  4. 我们知道Service是由 Connector 和 Engine 组成,所以在StandardService只要分了两条线去执行,一条线是通过 engine.start 去启动相关容器:如Host,Context,Wrapper,另一条先是通过Connector#start去启动 Endpoint ,监听Socket请求,默认采用NIO模式
  5. 另外一个点是在ContainerBase#startInternal中发布 LifecycleEvent 事件,通过后台线程去发布项目

所以总结下来我们发现Tomcat的架构很高级,用到了:工厂模式,模板方法,组合模式,双亲委派(自定义类加载器实现app隔离),责任链模式,NIO编程。真正做到了高内聚低耦合。下面贴一下Tomat启动流程和请求执行消息流程图

文章就写到这里咯,如果文章对你有所帮助,不要吝啬你的小赞赞。

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨家巨子@俏如来

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值