Tomcat(三)Tomcat启动过程

一 Tomcat启动机制

Tomcat启动机制图

二 Tomcat一键启停机制——LifeCycle

Tomcat生命周期图

2.1 什么是LifeCycle

  • LifeCycle接口定义如下,每个具体的组件去实现这些方法。
public interface Lifecycle {
    // 添加监听器
    public void addLifecycleListener(LifecycleListener listener);
    // 获取所以监听器
    public LifecycleListener[] findLifecycleListeners();
    // 移除某个监听器
    public void removeLifecycleListener(LifecycleListener listener);
    // 初始化方法
    public void init() throws LifecycleException;
    // 启动方法
    public void start() throws LifecycleException;
    // 停止方法,和start对应
    public void stop() throws LifecycleException;
    // 销毁方法,和init对应
    public void destroy() throws LifecycleException;
    // 获取生命周期状态
    public LifecycleState getState();
    // 获取字符串类型的生命周期状态
    public String getStateName();
}
  • LifeCycle接口定义分为两部分,一部分是组件的生命周期方法,比如init()、start()、stop()、destroy()。另一部分是扩展接口就是状态和监听器。
  • 因为所有的组件都实现了LifeCycle接口,如果可以在父组件的init()方法里创建子组件并调用子组件的init()方法,在父组件的start()方法里调用子组件的start()方法,那么调用者就可以无差别的调用各组件的init()方法和start()方法,并且只要调用最顶层组件,也就是Server组件的init()和start()方法,整个Tomcat就被启动起来了。

2.2 LifeCycleBase抽象类

  • 定义一个基类来实现共同的逻辑,然后让各个子类去继承它,就达到了重用的目的。基类中往往会定义一些抽象方法,并调用这些方法来实现骨架逻辑,抽象方法是留给各个子类去实现,如下图所示:
    生命周期继承图
  • LifeCycleBase实现了LifeCycle接口中所有的方法,还定义了相应的抽象方法交给具体子类去实现,这是典型的模板设计模式。
  • 简单看一下LifeCycleBase的init方法,如下:

2.3 LifeCycle扩展——事件监听机制

  • 把组件的生命周期定义成一个个状态,把状态的转变看作是一个事件。而事件是有监听器的,在监听器里可以实现一些逻辑,并且监听器也可以方便的添加和删除,这就是典型的观察者模式。
  • Tomcat定义一个枚举LifeCycleState来表示组件有哪些状态。
    生命周期状态枚举图
  • 下面我们来看一下在上述init方法中触发事件监听的方法setStateInternal:
private synchronized void setStateInternal(LifecycleState state,
        Object data, boolean check) throws LifecycleException {
    if (log.isDebugEnabled()) {       log.debug(sm.getString("lifecycleBase.setState", this, state));
    }
    if (check) {
        // Must have been triggered by one of the abstract methods (assume
        // code in this class is correct)
        if (state == null) {
            invalidTransition("null");
            return;// Any method can transition to failed
        // startInternal() permits STARTING_PREP to STARTING
        // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
        // STOPPING
        if (!(state == LifecycleState.FAILED ||
                (this.state == LifecycleState.STARTING_PREP &&
                        state == LifecycleState.STARTING) ||
                (this.state == LifecycleState.STOPPING_PREP &&
                        state == LifecycleState.STOPPING) ||
                (this.state == LifecycleState.FAILED &&
                        state == LifecycleState.STOPPING))) {
            // No other transition permitted
            invalidTransition(state.name());
        }
    }
    // 设置状态
    this.state = state;
    // 触发事件
    String lifecycleEvent = state.getLifecycleEvent();
    if (lifecycleEvent != null) {
        fireLifecycleEvent(lifecycleEvent, data);
    }
} 
protected void fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event = new LifecycleEvent(this, type, data);
    // lifecycleListeners为监听器数组
    for (LifecycleListener listener : lifecycleListeners) {
        // 触发监听器的监听逻辑
        listener.lifecycleEvent(event);
    }
}
  • 监听器注册两种情况:
    • Tomcat自定义了一些监听器:这些监听器是父组件在创建子组件的过程中注册到子组件的。比如MemoryLeakTrackingListener监听器,用来检测 Context容器中的内存泄漏,这个监听器是Host容器在创建Context容器时注册到Context中的
    • 在server.xml中定义自己的监听器,Tomcat在启动时会解析 server.xml,创建监听器并注册到容器组件

三 Bootstrap启动入口

3.1 main()

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. 初始化Bootstrap,主要包括实例化Tomcat特有的类加载器和Catalina实例
                bootstrap.init();
            } catch (Throwable t) {                handleThrowable(t);               t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else {                                   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);
            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);
        t.printStackTrace();
        System.exit(1);
    }
}
  • 核心方法就是调用init方法初始化deamon成员变量,以及当接收到start命令时,调用deamon的load和start方法启动。

3.2 init()

  • Bootstrap的init方法:
public void init() throws Exception {
    // 1. 初始化Tomcat类加载器
    initClassLoaders();    Thread.currentThread().setContextClassLoader(catalinaLoader);  SecurityClassLoad.securityClassLoad(catalinaLoader);
    // Load our startup class and call its process() method
    if (log.isDebugEnabled())
        log.debug("Loading startup class");
    // 2. 实例化Catalina实例
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();
    // 3. 反射调用Catalina的setParentClassLoader方法,将sharedLoader设置为Catalina的parentClassLoader成员变量
    if (log.isDebugEnabled())
        log.debug("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;
    Method method =       startupInstance.getClass().getMethod(methodName, paramTypes);
    method.invoke(startupInstance, paramValues);
    catalinaDaemon = startupInstance;
}
  • 作用:创建commonLoader、catalinaLoader、sharedLoader类加载器;实例化org.apache.catalina.startup.Catalina对象,并赋值给静态成员 catalinaDaemon,以sharedLoader作为入参通过反射调用该对象的 setParentClassLoader方法

3.3 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;
    }
    Method method =     catalinaDaemon.getClass().getMethod(methodName, paramTypes);
    if (log.isDebugEnabled()) {
        log.debug("Calling startup class " + method);
    }
    method.invoke(catalinaDaemon, param);
}
  • 作用:通过反射调用catalinaDaemon对象的load方法,catalinaDaemon对象就是在上面的init方法中实例化的。Catalina的load方法,完成一些Tomcat组件的初始化操作。

3.4 start()

public void start() throws Exception {
    if (catalinaDaemon == null) {
        init();
    }
    Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
    method.invoke(catalinaDaemon, (Object [])null);
}
  • 作用:通过反射调用catalinaDaemon对象上的start方法,该start方法,完成了Tomcat的启动。

四 Catalina

  • 通过上面的介绍我们知道,Bootstrap的main方法中,就是调用了Catalina的load和start方法,从而完成了Tomcat的启动。

4.1 load()

public void load() {
    if (loaded) {
        return;
    }
    loaded = true;
    long t1 = System.nanoTime(); 
    initDirs(); 
    // Before digester - it may be needed
    initNaming();
    // 1. 解析server.xml,实例化各Tomcat组件
    parseServerXml(true);
    Server s = getServer();
    if (s == null) {
        return;
    } 
    // 2. 为Server组件实例设置Catalina相关成员value
    getServer().setCatalina(this);    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());   getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    // Stream redirection
    initStreams(); 
    // 3. 调用Server组件的init方法,初始化Tomcat各组件
    try {
        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(sm.getString("catalina.initError"), e);
        }
    } 
    if(log.isInfoEnabled()) {        log.info(sm.getString("catalina.init", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
    }
}
  • Catalina的init方法作用:完成server.xml的解析以及Tomcat各组建的初始化。
    • parseServerXml()方法完成了xml配置到Tomcat各组件的Java对象的转化。
    • getServer().init()方法,使用了我们上面介绍的LifeCycle机制,完成了Server及以下组件的初始化

4.2 start()

public void start() {
    if (getServer() == null) {
        load();
    }
    if (getServer() == null) {        log.fatal(sm.getString("catalina.noServer"));
        return;
    }
    long t1 = System.nanoTime();
    // 1. start 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;
    } 
    if (log.isInfoEnabled()) {       log.info(sm.getString("catalina.startup", Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
    }
    // Register shutdown hook
    if (useShutdownHook) {
        if (shutdownHook == null) {
            shutdownHook = new CatalinaShutdownHook();
        }        Runtime.getRuntime().addShutdownHook(shutdownHook);
        LogManager logManager = LogManager.getLogManager();
        if (logManager instanceof ClassLoaderLogManager) {
            ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                    false);
        }
    } 
    if (await) {
        await();
        stop();
    }
}

主要是调用Server组件的start方法,启动Tomcat的所有组件。

到这里我们知道,Tomcat是通过调用Catalina的load()和start()方法,完成了Tomcat的启动。load()方法负责组件的初始化,start()方法负责组件的启动。关于Tomcat组件的初始化,其中Host及其下级组件的初始化比较特殊,并没有在Engine的初始化方法中完成Host及下级组件的初始化。另外Context的实例化和启动过程也比较特殊(在上述parseServerXml方法中,并没有实例化,是在Host组件启动时动态生成的),另外我们知道Tomcat web应用支持热更新,也与Context的启动策略有关。关于Context实例化和初始化过程,我们先挖个坑,本篇文章中不做详细介绍,在后面用专门的文章详细介绍。

五 Server

  • Server组件的init和start方法:我们知道LifeCycle机制,init方法其实最终调用的是StandardServer的initInternal方法,start方法最终调用的是StandardServer的initInternal方法。

5.1 initInternal()

protected void initInternal() throws LifecycleException {
    // 1. 调用LifeCycleMBeanBase的initInternal方法,注册JMX Mbean
    super.initInternal();
    // Initialize utility executor    reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
    register(utilityExecutor, "type=UtilityExecutor");
    // 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"); 
    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();
        }
    }
    // 2. 调用所有子组件Service的init方法,初始化service
    for (Service service : services) {
        service.init();
    }
}

5.2 startInternal()

protected void startInternal() throws LifecycleException {
    // 1. 触发LifeCycle事件监听  fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);
    globalNamingResources.start();
    // 2. 启动所有Server的子组件Service
    synchronized (servicesLock) {
        for (Service service : services) {
            service.start();
        }
    } 
    if (periodicEventDelay > 0) {
        monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
                new Runnable() {
                    @Override
                    public void run() {                       startPeriodicLifecycleEvent();
                    }
                }, 0, 60, TimeUnit.SECONDS);
    }
}

六 Service

6.1 initInternal()

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();
    // Initialize our defined Connectors
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            connector.init();
        }
    }
}
  • 往jmx中注册StandardService
    初始化Engine
  • 存在Executor线程池,进行init操作,这个Excecutor是tomcat的接口,继承至java.util.concurrent.Executor和org.apache.catalina.Lifecycle
  • 初始化Connector连接器,默认有http1.1、ajp连接器,而这个Connector初始化过程,又会对ProtocolHandler进行初始化

6.2 startInternal()

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
    if (engine != null) {
        synchronized (engine) {
            engine.start();
        }
    } 
    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    } 
    mapperListener.start();
    // Start our defined Connectors second
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
                connector.start();
            }
        }
    }
}
  • 跟上述initInternal方法比较类似,只不过是调用各子组件(Engine、Connector)的start方法,启动子组件。

七 Engine

7.1 initInternal()

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

Engine的init()初始化方法中,并没有去调用子组件Host的init方法去初始化Host,在Host的start方法中进行Host组件的初始化,然后再进行启动操作。

  • 出于效率考虑。Host、Context、Wrapper这些容器和具体的webapp应用相关联了,初始化过程会更加复杂耗时,在start阶段用多线程完成初始化以及start生命周期,效率可能会更高。

7.2 startInternal()

protected synchronized 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方法。
protected synchronized void startInternal() throws LifecycleException { 
    // 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<>();
    for (Container child : children) {        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);
    }
}
  • ContainerBase中,使用了多线程来启动子组件。并且在最后通过setState(LifecycleState.STARTING)触发了事件监听。
  • 启动子组件,使用了线程池,将子组件的启动任务提交到线程池。
private static class StartChild implements Callable<Void> {
    private Container child;
    public StartChild(Container child) {
        this.child = child;
    } 
    @Override
    public Void call() throws LifecycleException {
        child.start();
        return null;
    }
}
  • 线程池中执行的任务最终调用的还是子组件(Host)的start()方法。我们来看一下Host的start()方法,其实Host的start方法是继承自ContainerBase的,如下:
public final synchronized void start() throws LifecycleException {
    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||            LifecycleState.STARTED.equals(state)) { 
        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();            log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
        } else if (log.isInfoEnabled()) {            log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
        }
        return;
    } 
    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&            !state.equals(LifecycleState.STOPPED)) {       invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }
    try {        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
            // This is a 'controlled' failure. The component put itself into the
            // FAILED state so call stop() to complete the clean-up.
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        // This is an 'uncontrolled' failure so put the component into the
        // FAILED state and throw an exception.
        handleSubClassException(t, "lifecycleBase.startFail", toString());
    }
}
  • 如果如果state为LifecycleState.NEW,会先进性初始化,然后在调用startInternal启动组件。这就是我们上面介绍的,Engine并没有在init方法中初始化子组件Host,而是在启动过程start方法中初始化的。

八 Host

8.1 initInternal()

protected void initInternal() throws LifecycleException {    reconfigureStartStopExecutor(getStartStopThreads());
    super.initInternal();
}
  • Host的init方法中也没有初始化子组件Context,Context组件的初始化跟随者Context的启动start过程初始化的。

8.2 startInternal()

protected synchronized 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 =
                    (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();
}

可以看到,Host的start方法中,仅仅是为Host的piline添加了一些Valve,随后调用了父类ContainerBase的startInternal方法。虽然ContainerBase的startInternal方法中可以启动子组件,但好像server.xml中并么有配置Host子组件Context相关的信息,所以也没办法去启动Context组件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

weixin_42242792

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值