从servlet到springboot(14) Tomcat源码分析(1)。tomcat的启动与Lifecycle接口

56 篇文章 2 订阅

前面几篇内容主要几种在springboot启动时,我们接下去会探讨下请求到达spring容器的过程。

请求进入spring容器最先会进入tomcat,所以我们优先分析下tomcat

 

如图所示:tomcat的顶层容器是Server,一个tomcat顶多只有一个Server,一个Server下有多个Service,一个Service又包含多个Connectors和一个Container。Connectors用于处理连接欸相关的事情,并提供Socket与request转换,Container用于封装和管理Servlet以及处理具体的request请求。

Bootstrap是Tomcat的入口,正常情况下启动Tomcat就是调用Bootstrap的main方法

 

synchronized (daemonLock) {
    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;

。。。。。

bootstrap.init(); 这里会调用bootstrap的init方法

public void init() throws Exception {

    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");
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();

    // Set the shared extensions class loader
    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;

}

这段代码主要初始化了一个classLoader,然后把这个classloader设置在当前线程上(这是因为双亲委托这个模型,具体可以百度双亲委托模型)。随后用这个classLoader创建了Catalina实例,然后赋值给catalinaDaemon,后续的命令都用catalinaDaemon来执行。

我们回到bootstrap的main方法

try {
 。。。。
    } else if (command.equals("start")) {
        daemon.setAwait(true);
        daemon.load(args);
        daemon.start();
      。。。。
   。。。。。
    } else {
        log.warn("Bootstrap: command \"" + command + "\" does not exist.");
    }

这里通过 daemon.start();启动tomcat

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

    Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
    method.invoke(catalinaDaemon, (Object [])null);

}
通过反射来调用catalinaDaemon的start方法

我们接下去主要分析catalinaDaemon的start方法

public void start() {

    if (getServer() == null) {
        load();
    }

    if (getServer() == null) {
        log.fatal(sm.getString("catalina.noServer"));
        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(sm.getString("catalina.startup", Long.valueOf((t2 - t1) / 1000000)));
    }

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

首先会调用 load();方法去加载server.xml中文件中的配置内容,初始化一个server。

随后会通过getServer().start()这句话来启动server容器

我们跟踪进入这个方法

Tomcat默认的Server实现类是StanardServer,StanardServer继承了LifecycleMBeanBase,而LifecycleMBeanBase又继承了

LifecycleBase,start方法就定义在LifecycleBase中,init方法也定义在上面

我们先看下Server这个接口

关注到有一个addService和removeService方法。我们之前说过一个server中会包含多个Service,addService就是在Server中添加Service的。

我们进入LifecycleBase的start方法

  。。。。。
    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)){
        stop();
    。。。。

   。。。。。。
        startInternal();
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.startFail",toString()), t);
    }

    if (state.equals(LifecycleState.FAILED) ||
            state.equals(LifecycleState.MUST_STOP)) {
        stop();
    } else {
        // Shouldn't be necessary but acts as a check that sub-classes are
        // doing what they are supposed to.
        if (!state.equals(LifecycleState.STARTING)) {
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        }

        setStateInternal(LifecycleState.STARTED, null, false);
    }
}

省略前面一大串废话,我们看到init。点击进入

@Override
public final synchronized void init() throws LifecycleException {
。。。。
        initInternal();
。。。。

再进入initInternal();方法

protected abstract void initInternal() throws LifecycleException;是一个模板方法,会由子类自己去实现,我们进入standardServer的initInternal方法
@Override
protected void initInternal() throws LifecycleException {

 。。。。。。废话一大堆
    // Initialize our defined Services
    for (int i = 0; i < services.length; i++) {
        services[i].init();
    }
}

前面一大串都忽略掉,直接看最后一句话

for (int i = 0; i < services.length; i++) { services[i].init(); }会发现这里是循环调用每一个services的init方法

然后看到 startInternal();这也是一个模板方法,我们看到standardServer的实现

protected void startInternal() throws LifecycleException {
。。。。。。
    synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
    }
}

和前面的init一样,也是循环调用每一个service的start方法

 

我们回到catalina的start方法

注意到start方法的最后又一句话

if (await) {
    await();
    stop();
}

我们点击最终进入的是StandardServer的await方法

public void await() {
    // Negative values - don't wait on port - tomcat is embedded or we just don't like ports
    if( port == -2 ) {
        // undocumented yet - for embedding apps that are around, alive.
        return;
    }
    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 {
        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;
                try {
                    socket = serverSocket.accept();
                    socket.setSoTimeout(10 * 1000);  // Ten seconds
                    stream = socket.getInputStream();
                } 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;
                    }
                    if (ch < 32)  // Control character or EOF terminates loop
                        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
            }
        }
    }
}

一大串方法。归纳一下就做了一件事,让服务器进入端口监听状态

 

我们接下去分析下service的start方法

service也同样继承了LifecycleMBeanBase,而LifecycleMBeanBase又继承了LifecycleBase

所以start方法最终还是会去调用service默认实现类的initInternal和startInternal方法

tomcat默认实现的service是StandardService

@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    if (container != null) {
        container.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) {
            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);
            }
        }
    }
}

initInternal可以看到主要是调用container,excutors,mapperListener,connectors的init方法,因为connectors在一个service中有多个,所以会循环调用每一个connectors的init方法,container和connectors我们之前已经讲过了,mapperListener是Mapper的监听器,可以监听container容器的变化,executors是用在connectors中管理线程的线程池。

我们可以用这样一张时序图来演示tomcat的启动过程

-----------------------------------------------------------------------

上面我们刚刚分析完tomcat启动的整体流程,我们可以发现,server,service的start方法都是调用lifecycle的start方法,最终是调用默认实现类的initInternal和startInternal方法,其实我们下一篇会说到的container和connectors的start方法也是一样的。这里说明lifecycle是一个非常重要的接口,它控制着tomcat的生命周期

我们直接看lifecycle接口

首先这个接口定义了13个Stirng常量,用于各种事件的type属性中,(具体可以看lifecycleEvent接口,这里不细说)。然后定义了三个管理监听器的方法addLifecycleListener。。。。findLifecyleListener。。。removeLifecycleListner。。。。然后就是4个生命周期相关的方法,init,start,stop,destory,最后就是获取当前状态的两个方法,getState和getStateName,用来获取当前的状态

LifecycleBase是Lifecycle的默认实现,这里我们不细说三个管理监听器的方法。

生命周期的方法都调用了各自的模板方法,比如init调用了initInternal模板方法,start调用了startInternal方法。

我们找到start方法来分析一下

@Override
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);
    }

    setStateInternal(LifecycleState.STARTING_PREP, null, false);

    try {
        startInternal();
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.startFail",toString()), t);
    }

    if (state.equals(LifecycleState.FAILED) ||
            state.equals(LifecycleState.MUST_STOP)) {
        stop();
    } else {
        // Shouldn't be necessary but acts as a check that sub-classes are
        // doing what they are supposed to.
        if (!state.equals(LifecycleState.STARTING)) {
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        }

        setStateInternal(LifecycleState.STARTED, null, false);
    }

其实四个方法的形式都是类似的,都是先判断下状态,如果符合就调用相应的模板方法,不符合就抛异常。。

 

先分析到这里,下一篇将分析下Container和Connectors

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值