4、启动

从上一节中我们分析了组件的初始化,除了容器进行了线程池的创建之后,其他的组件的初始化基本上是在注册mbean服务。

Bootstrap

daemon.setAwait(true);
daemon.load(args);
daemon.start();

我们分析了load,那么解析来就开始start

public void start()
        throws Exception {
    //如果还没有创建Catalina,那么要进行初始化,确保已经初始化
    if( catalinaDaemon==null ) init();

    Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
    //反射调用Catalina的start方法
    method.invoke(catalinaDaemon, (Object [])null);

}

Catalina.start()

public void start() {
        //确保Catalina已经进行加载
        if (getServer() == null) {
            load();
        }
        //如果加载后依然么有创建服务,那么只能停止,打印日志告知没有配置Server
        if (getServer() == null) {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }

        long t1 = System.nanoTime();

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

在分析服务启动前,我们先来分析下服务的阻塞,一个服务启动后不能立马退出,那么就应当进行阻塞

public void org.apache.catalina.startup.Catalina.await() {

        getServer().await();

    }
public void StandardServer.await() {
        // Negative values - don't wait on port - tomcat is embedded or we just don't like ports
        //如果端口指定为-2,那么不进行阻塞,直接返回
        if( port == -2 ) {
            // undocumented yet - for embedding apps that are around, alive.
            return;
        }
        //如果端口为-1,那么通过循环加sleep进行阻塞
        if( port==-1 ) {
            try {
                awaitThread = Thread.currentThread();
                while(!stopAwait) {
                    try {
                        Thread.sleep( 10000 );
                    } catch( InterruptedException ex ) {
                        // continue and check the flag
                    }
                }
            } finally {
                awaitThread = null;
            }
            return;
        }

        // Set up a server socket to wait on
        try {
            //如果指定了端口,那么打开一个socket
            awaitSocket = new ServerSocket(port, 1,
                    InetAddress.getByName(address));
        } catch (IOException e) {
            log.error("StandardServer.await: create[" + address
                               + ":" + port
                               + "]: ", e);
            return;
        }

        try {
            awaitThread = Thread.currentThread();

            // Loop waiting for a connection and a valid command
            while (!stopAwait) {
                ServerSocket serverSocket = awaitSocket;
                if (serverSocket == null) {
                    break;
                }

                // Wait for the next connection
                Socket socket = null;
                StringBuilder command = new StringBuilder();
                try {
                    InputStream stream;
                    long acceptStartTime = System.currentTimeMillis();
                    try {
                        //BIO阻塞
                        socket = serverSocket.accept();
                        socket.setSoTimeout(10 * 1000);  // Ten seconds
                        stream = socket.getInputStream();
                    } catch (SocketTimeoutException ste) {
                        // This should never happen but bug 56684 suggests that
                        // it does.
                        log.warn(sm.getString("standardServer.accept.timeout",
                                Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);
                        continue;
                    } catch (AccessControlException ace) {
                        log.warn("StandardServer.accept security exception: "
                                + ace.getMessage(), ace);
                        continue;
                    } catch (IOException e) {
                        if (stopAwait) {
                            // Wait was aborted with socket.close()
                            break;
                        }
                        log.error("StandardServer.await: accept: ", e);
                        break;
                    }

                    // Read a set of characters from the socket
                    int expected = 1024; // Cut off to avoid DoS attack
                    while (expected < shutdown.length()) {
                        if (random == null)
                            random = new Random();
                        expected += (random.nextInt() % 1024);
                    }
                    //接收命令
                    while (expected > 0) {
                        int ch = -1;
                        try {
                            ch = stream.read();
                        } catch (IOException e) {
                            log.warn("StandardServer.await: read: ", e);
                            ch = -1;
                        }
                        // Control character or EOF (-1) terminates loop
                        if (ch < 32 || ch == 127) {
                            break;
                        }
                        command.append((char) ch);
                        expected--;
                    }
                } finally {
                    // Close the socket now that we are done with it
                    try {
                        if (socket != null) {
                            socket.close();
                        }
                    } catch (IOException e) {
                        // Ignore
                    }
                }

                // Match against our command string
                //如果匹配到了关闭命令,那么跳出循环
                boolean match = command.toString().equals(shutdown);
                if (match) {
                    log.info(sm.getString("standardServer.shutdownViaPort"));
                    break;
                } else
                    log.warn("StandardServer.await: Invalid command '"
                            + command.toString() + "' received");
            }
        } finally {
            ServerSocket serverSocket = awaitSocket;
            awaitThread = null;
            awaitSocket = null;

            // Close the server socket and return
            if (serverSocket != null) {
                try {
                    //退出
                    serverSocket.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    }

tomcat中有一个shutdow的批处理文件,双击这个文件就会打开一个暂时的窗口,它给8005端口发送一个SHUTDOWN命令,让tomcat主线程退出,以达到关闭的目的

getServer().start()

public final synchronized void org.apache.catalina.util.LifecycleBase.start() throws LifecycleException {
    	//如果已经正在启动了,那么无需重复启动,直接返回,由于多线程的原因,这个state是volatile修饰的,保证内存可见性
        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;
        }
        //如果当前状态还是NEW,也就是连init都么有,那么就需要进行初始化
        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.
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
        }
    }
protected void org.apache.catalina.core.StandardServer.startInternal() throws LifecycleException {
    	//CONFIGURE_START_EVENT = configure_start
    	//触发configure_start事件
        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        //设置starting状态,并触发事件
        setState(LifecycleState.STARTING);
        //启动globalNamingResources
        globalNamingResources.start();

        // Start our defined Services
        //启动Service
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();
            }
        }
    }

可以看到和init时调用的顺序差不多,server.start->globalNamingResources.start->service.start
这一路比较平静没发生什么大风大浪,我们直接到service.start方法中看看吧

service.start

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

        synchronized (executors) {
            for (Executor executor: executors) {
                //启动线程池
                executor.start();
            }
        }

        mapperListener.start();

        // Start our defined Connectors second
        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);
                }
            }
        }
    }

我们先来看看Engine的启动

一贯的先调用LifecycleBase的start方法,设置状态,循环调用生命周期监听器,感兴趣的监听器会做某些处理,最后调用StandardEngine的startInternal, StandardEngine的最后调用StandardEngine的startInternal又会去调用ContainerBase实现的startInternal方法,这个方法是所有容器类通用的方法,所以对于后面的容器启动,不会再次赘述,但会提示调用了此方法。

 protected synchronized void ContainerBase.startInternal() throws LifecycleException {

        // Start our subordinate components, if any
        logger = null;
        getLogger();
        //启动集群组件
        Cluster cluster = getClusterInternal();
        if (cluster instanceof Lifecycle) {
            ((Lifecycle) cluster).start();
        }
        //启动Realm身份认证
        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 (int i = 0; i < children.length; i++) {
            //对于StandardEngine,那么这里启动的就是子容器StandardHost
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }

        MultiThrowable multiThrowable = new MultiThrowable();

        for (Future<Void> result : results) {
            try {
                //阻塞,等待子容器全部启动完毕
                result.get();
            } catch (Throwable e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                multiThrowable.add(e);
            }

        }
        if (multiThrowable.size() > 0) {
            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
        //启动后台进程
        threadStart();
    }

从上面的代码可以看到,tomcat对于子容器的启动都是通过线程池的方式去异步启动的,然后主线程通过Future.get进行阻塞,等待子容器启动完毕,so我们来看看StandardHost的启动

 public final synchronized void start() throws LifecycleException {
    	//如果已经正在启动了,那么无需重复启动,直接返回,由于多线程的原因,这个state是volatile修饰的,保证内存可见性
        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;
        }
        //如果当前状态还是NEW,也就是连init都么有,那么就需要进行初始化
        //很明显Host这个容器在前面没有进行过初始化,所以这里肯定会进行初始化
        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.
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
        }
    }

上一节中我们没有看到Host与Context的初始化,从上面的代码中我们可以看到在start的时候会对当前容器状态做判断,如果还是NEW这种状态,那么就会先初始化,其实Host与COntext的初始化与Engine相比并没有做特殊的操作,他们跟Host一样都是在ContainerBase中创建了一个线程池,然后再调用LifecycleBase进行MBean的注册。所以这里就不对Host与Context容器的初始化做过多的分析,我们直接分析start

protected synchronized void StandardHost.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);
            }
        }
        //调用ContainerBase的startInternal方法
        super.startInternal();
    }

StandardHost的startInternal调用super.startInternal(),意味着将要调用StandardContext的start方法(当然,这里Host的可能是空的,因为我们在server.xml中一般不会配置context标签,一般都是直接放到appbase下面),StandardContext的启动非常复杂,我们单独开一个章节去分析它,这里先跳过。那么接下来,tomcat执行到了HostConfig感兴趣的before_start事件,其代码如下

public void org.apache.catalina.startup.HostConfig.beforeStart() {
        //是否创建目录
        if (host.getCreateDirs()) {
            //根据config.xml配置好的属性或者默认的配置路径进行路径的创建,一般AppBase为$TOMCAT_BASE/webapps,appBase我们可以在配置文件中指定
            //configBaseFile为$TOMCAT_BASE/Engine名(一般为Catalina)/conf/host名
            File[] dirs = new File[] {host.getAppBaseFile(),host.getConfigBaseFile()};
            for (int i=0; i<dirs.length; i++) {
                if (!dirs[i].mkdirs() && !dirs[i].isDirectory()) {
                    log.error(sm.getString("hostConfig.createDirs",dirs[i]));
                }
            }
        }
    }

beforestart阶段主要就是判断appBase与当前host的基本配置文件夹是否存在,如果不存在那么就进行创建,本节先到这里,下一节继续查host的start事件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值