Tomcat04——源码跟踪tomcat的初始化与启动流程

1. 源码入口

在bin目录下的catalina.bat中,有如下代码 set MAINCLASS=org.apache.catalina.startup.Bootstrap ,表明了tomcat的主类也就是启动类的位置。

启动类位置:org/apache/catalina/startup/Bootstrap.java

2. 初始化流程

2.1 主类初始化

2.1.1 调用init()方法进行初始化

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

2.1.2 组类init()方法

init的作用主要是创建一个Catalina对象

只看中文注释部分的主要代码

 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");

        // 通过反射创建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);

        // 将Catalina对象startupInstance赋值给catalinaDaemon
        catalinaDaemon = startupInstance;
    }

2.1.3 主类初始化之后执行load方法

主类初始化结束之后,就开始执行一些命令参数语句

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

通过命令参数,找到对应的load方法,这里以start为例:

 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);
        }
        // 调用catalina的load方法
        method.invoke(catalinaDaemon, param);
    }

2.2 Catalina

接着执行catalina的load方法

public void load(String args[]) {

        try {
            if (arguments(args)) {
                // 调用load的重载方法,执行逻辑
                load();
            }
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }

然后有参的load调用无参的load方法

getServer().init();

2.2.1 创建tomcat的xml解析工具

// Digester是tomcat的xml文件解析根据,解析server.xml
Digester digester = createStartDigester();

2.2.2 得到Server对象

经过一系列的处理之后,在执行

getServer().init();

得到Server对象,然后调用Server对象的init()方法

2.3 Server

在tomcat的组件中,有一个生命周期的接口LifeCycle,其有一个实现类LifeCycleBase,getServer().init();调用的init其实就是LifeCycleBase中的init方法。

 public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }

        try {
            setStateInternal(LifecycleState.INITIALIZING, null, false);
            // 初始化
            initInternal();
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            handleSubClassException(t, "lifecycleBase.initFail", toString());
        }
    }

发现,在init中,又调用了initInternal方法。initInternal是LifeCycleBase的抽象方法,所以最终调用的这个方法就是实现类中的initInternal方法,也即是调用的Server的实现类StandardServer的initInternal。

进入initInternal方法,这里又调用了service的init方法

// 一个Server下可以有多个service
for (int i = 0; i < services.length; i++) {
   services[i].init();
}

2.4 Service

跟前面的过程一样,找到StandardService的initInternal,分别初始化引擎(engine)、执行器(Executor)、连接器(Connector)

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

2.5 Connector

在Service中初始化了很多组件,这些组件初始化都是差不多的,这里着重介绍一下Connector。

Connector不是接口,所以直接调用其方法initInternal。

2.5.1 创建适配器

adapter = new CoyoteAdapter(this);

2.5.2 初始化protocolHandler

try {
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }

3. 启动流程

在BootStrap主函数中,load方法下面,接着就是start方法,接下来我们顺着代码看一下tomcat的启动流程。

daemon.start();

首先,调用自身的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方法,在去调用Server的start方法

try {
   getServer().start();
}

又采用了模板设计模式,走到了LifeCycleBase中,在start方法中通过startInternal方法执行各个接口的实现类的启动业务逻辑。

这里在重点说下Connector连接器对象的启动,在Connector中调用startInternal方法,然后调用protocolHandler的start方法

try {
  protocolHandler.start();
} 

最后程序会走到AbsTractEndpoint的start方法

public final void start() throws Exception {
        if (bindState == BindState.UNBOUND) {
            bind();
            bindState = BindState.BOUND_ON_START;
        }
        startInternal();
    }

执行startInternal()方法,因为是Http/1.1,所以会执行NioEndpoint的startInternal方法,在该方法的最后面,有一个开启线程的调用

// 开启线程
startAcceptorThreads();
protected final void startAcceptorThreads() {
        int count = getAcceptorThreadCount();
        // 接受客户端的请求对象
        acceptors = new Acceptor[count];

        // 开启线程
        for (int i = 0; i < count; i++) {
            // 创建线程对象
            acceptors[i] = createAcceptor();
            String threadName = getName() + "-Acceptor-" + i;
            acceptors[i].setThreadName(threadName);
            Thread t = new Thread(acceptors[i], threadName);
            t.setPriority(getAcceptorThreadPriority());
            t.setDaemon(getDaemon());
            t.start();
        }
    }

 createAcceptor():

protected AbstractEndpoint.Acceptor createAcceptor() {
        return new Acceptor();
    }

Acceptor():

protected class Acceptor extends AbstractEndpoint.Acceptor {

        @Override
        public void run() {

            int errorDelay = 0;

            // Loop until we receive a shutdown command
            while (running) {

                // Loop if endpoint is paused
                while (paused && running) {
                    state = AcceptorState.PAUSED;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                if (!running) {
                    break;
                }
                state = AcceptorState.RUNNING;

                try {
                    //if we have reached max connections, wait
                    countUpOrAwaitConnection();

                    SocketChannel socket = null;
                    try {
                        // Accept the next incoming connection from the server
                        // socket
                        socket = serverSock.accept();
                    } catch (IOException ioe) {
                        // We didn't get a socket
                        countDownConnection();
                        if (running) {
                            // Introduce delay if necessary
                            errorDelay = handleExceptionWithDelay(errorDelay);
                            // re-throw
                            throw ioe;
                        } else {
                            break;
                        }
                    }
                    // Successful accept, reset the error delay
                    errorDelay = 0;

                    // Configure the socket
                    if (running && !paused) {
                        // setSocketOptions() will hand the socket off to
                        // an appropriate processor if successful
                        if (!setSocketOptions(socket)) {
                            closeSocket(socket);
                        }
                    } else {
                        closeSocket(socket);
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }
            }
            state = AcceptorState.ENDED;
        }

在Acceptor的run方法中,有一个一句话是用来接收客户端的请求的代码,如下:

socket = serverSock.accept();

接收并等待客户端的请求。

至此,启动流程结束!

总结:

        从启动流程图与源码跟踪,可以看出tomcat的启动过程非常标准化,统一按照生命周期管理接口LifeCycle的定义进行启动的,首先调用init()方法进行组件的逐级初始化操作,然后再调用start()方法进行启动。

        每一级的组件除了完成自身的处理外,还要负责调用子组件响应的生命周期管理方法,组件与组件之间是松耦合的,因为我们可以很容易的通过配置文件进行修改和替换。

发布了128 篇原创文章 · 获赞 6 · 访问量 3220

猜你喜欢

转载自blog.csdn.net/weixin_43318134/article/details/103941160
今日推荐