Tomcat源码走读 06-tomcat的容器启动过程

本文详细解析了Tomcat容器的启动流程,包括StandardServer、StandardService、StandardEngine及Connector的启动内部机制,深入探讨了war包解压、web项目部署及acceptor线程的启动过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

06-tomcat的容器启动过程

在02-tomcat的启动和关闭流程中,在容器初始化之后,Catalina.start()方法会触发容器的启动流程。

public void start() {
        try {
            getServer().start();
        } catch (LifecycleException e) {
        }
    }

1. StandardServer.start() ==> StandardServer.startInternal()

protected void startInternal() throws LifecycleException {
        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);
        globalNamingResources.start();
        synchronized (services) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();
            }
        }
    }

2. StandardService.start() ==> StandardService.startInternal()

protected void startInternal() throws LifecycleException {
        setState(LifecycleState.STARTING);
        if (container != null) {
            synchronized (container) {
                container.start(); //启动container
            }
        }
        synchronized (executors) {
            for (Executor executor: executors) {
                executor.start(); //启动executor
            }
        }
        synchronized (connectors) {
            for (Connector connector: connectors) {
                try {
                    if (connector.getState() != LifecycleState.FAILED) {
                        connector.start(); //启动connector
                    }
                } catch (Exception e) {
                }
            }
        }
    }

3. StandardEngine.startInternal()。==》 containerBase.startInternal()

protected synchronized void startInternal() throws LifecycleException {
        if ((loader != null) && (loader instanceof Lifecycle))
            ((Lifecycle) loader).start();
        logger = null;
        getLogger();
        if ((manager != null) && (manager instanceof Lifecycle))
            ((Lifecycle) manager).start();
        if ((cluster != null) && (cluster instanceof Lifecycle))
            ((Lifecycle) cluster).start();
        Realm realm = getRealmInternal();
        if ((realm != null) && (realm instanceof Lifecycle))
            ((Lifecycle) realm).start();
        if ((resources != null) && (resources instanceof Lifecycle))
            ((Lifecycle) resources).start();
        // Start our child containers, if any
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<Future<Void>>();
        for (int i = 0; i < children.length; i++) {
            results.add(startStopExecutor.submit(new StartChild(children[i]))); //启动webapp的地方
        }
        boolean fail = false;
        for (Future<Void> result : results) {
            try {
                result.get();
            } catch (Exception e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                fail = true;
            }
        }
        // Start the Valves in our pipeline (including the basic), if any
        if (pipeline instanceof Lifecycle)
            ((Lifecycle) pipeline).start();
        setState(LifecycleState.STARTING);
        threadStart();  //开启一个后台线程,扫描过期的session。
    }

4. StartChild启动webapp

4.1 要了解这块逻辑,我们需要先回到server.xml的解析Host的地方。
public void addRuleInstances(Digester digester) {
        digester.addObjectCreate(prefix + "Host",
                                 "org.apache.catalina.core.StandardHost",
                                 "className");
        digester.addSetProperties(prefix + "Host");
        digester.addRule(prefix + "Host",
                         new CopyParentClassLoaderRule());
        digester.addRule(prefix + "Host",
                         new LifecycleListenerRule
                         ("org.apache.catalina.startup.HostConfig",
                          "hostConfigClass")); //创建HostConfig生命周期监听器。
        digester.addSetNext(prefix + "Host",
                            "addChild",
                            "org.apache.catalina.Container");
        digester.addCallMethod(prefix + "Host/Alias",
                               "addAlias", 0);
        digester.addObjectCreate(prefix + "Host/Cluster",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties(prefix + "Host/Cluster");
        digester.addSetNext(prefix + "Host/Cluster",
                            "setCluster",
                            "org.apache.catalina.Cluster");
        digester.addObjectCreate(prefix + "Host/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties(prefix + "Host/Listener");
        digester.addSetNext(prefix + "Host/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");
        digester.addRuleSet(new RealmRuleSet(prefix + "Host/"));
        digester.addObjectCreate(prefix + "Host/Valve",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties(prefix + "Host/Valve");
        digester.addSetNext(prefix + "Host/Valve",
                            "addValve",
                            "org.apache.catalina.Valve");
    }

上面的创建HostConfig生命周期监听器的地方会在解析host节点的时候为host创建一个HostConfig的生命周期监听,记住这里,我们等下就要用到了。

4.2 现在我们回到StartChild这里,他是一个Callable。
private static class StartChild implements Callable<Void> {
        private Container child;
        public StartChild(Container child) {
            this.child = child;
        }
        public Void call() throws LifecycleException {
            child.start();
            return null;
        }
    }

他的child就是上面我们的StandardEngine的child,也就是StandardHost。所以接下来我们需要看StandardHost.startInternal(),因为这里面其他的逻辑跟我们这个流程关系不大,所以指定跳到他调用到了父类ContainerBase.startInternal()。
setState(LifecycleState.STARTING);
这里我们暂时只需要注意上面这一句。他最后调用LifecycleBase.setStateInternal()发送了一条Host生命周期切换的消息。
这时候我们上面创建的HostConfig接受到消息:

public void lifecycleEvent(LifecycleEvent event) {
        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) {
            return;
        }
        if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
            check();
        } else if (event.getType().equals(Lifecycle.START_EVENT)) {
            start(); //LifecycleState.STARTING(true, Lifecycle.START_EVENT)
        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            stop();
        }
    }
public void start() {
        try {
            ObjectName hostON = host.getObjectName();
            oname = new ObjectName
                (hostON.getDomain() + ":type=Deployer,host=" + host.getName());
            Registry.getRegistry(null, null).registerComponent
                (this, oname, this.getClass().getName());
        } catch (Exception e) {
        }
        if (host.getCreateDirs()) {
            File[] dirs = new File[] {appBase(),configBase()}; //appBase就是<Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true"> 
            for (int i=0; i<dirs.length; i++) {
                if (!dirs[i].mkdirs() && !dirs[i].isDirectory()) {
                    log.error(sm.getString("hostConfig.createDirs",dirs[i]));
                }
            }
        }
        if (host.getDeployOnStartup())
            deployApps();
    }

protected void deployApps() {
        File appBase = appBase();
        File configBase = configBase();
        String[] filteredAppPaths = filterAppPaths(appBase.list()); //获取appBase里面的项目,有examples,docs和你自己的项目等等。
        deployDescriptors(configBase, configBase.list()); //部署configBase
        deployWARs(appBase, filteredAppPaths); //部署war包,war包的解压在这里面
        deployDirectories(appBase, filteredAppPaths); //部署appBase里面的项目,一般我们的项目在这里
    }

protected void deployDirectories(File appBase, String[] files) {
        ExecutorService es = host.getStartStopExecutor(); //获取Host里面的线程池
        List<Future<?>> results = new ArrayList<Future<?>>();
        for (int i = 0; i < files.length; i++) {
            if (files[i].equalsIgnoreCase("META-INF"))
                continue;
            if (files[i].equalsIgnoreCase("WEB-INF"))
                continue;
            File dir = new File(appBase, files[i]);
            if (dir.isDirectory()) {
                ContextName cn = new ContextName(files[i], false);
                if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
                    continue;
                results.add(es.submit(new DeployDirectory(this, cn, dir))); //提交任务到线程池。
            }
        }
    }
4.3 DeployDirectory.run() ==> HostConfig.deployDirectory()
protected void deployDirectory(ContextName cn, File dir) {
        Context context = null;
        File xml = new File(dir, Constants.ApplicationContextXml);
        File xmlCopy = new File(configBase(), cn.getBaseName() + ".xml");
        DeployedApplication deployedApp;
        boolean copyThisXml = copyXML;
        try {
            if (deployXML && xml.exists()) {
           } else if (!deployXML && xml.exists()) {
                context = new FailedContext();
            } else {
                context = (Context) Class.forName(contextClass).newInstance(); //创建一个StandardContext对象。
            }
            Class<?> clazz = Class.forName(host.getConfigClass());
            LifecycleListener listener =
                (LifecycleListener) clazz.newInstance(); //创建ContextConfig监听
            context.addLifecycleListener(listener); //添加监听器
            context.setName(cn.getName());
            context.setPath(cn.getPath());
            context.setWebappVersion(cn.getVersion());
            context.setDocBase(cn.getBaseName());
            host.addChild(context); //把StandardContext加入到StandardHost的子集里面
        } catch (Throwable t) {
        } finally {
        }
        deployed.put(cn.getName(), deployedApp);
    }
4.4 StandardHost.addChild() ==> ContainerBase.addChild() ==> ContainerBase.addChildInternal() ==> StandardContext.start() ==> StandardContext.startInternal()
protected synchronized void startInternal() throws LifecycleException {
        if (getLoader() == null) {
            WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); //创建WebappLoader类加载器
            webappLoader.setDelegate(getDelegate());
            setLoader(webappLoader);
        }
        try {
            if (ok) {
                fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); //读取web.xml中的servlet等数据并封装成wrapper后addChild到StandardContext中。添加applicationListener等。
                for (Container child : findChildren()) { //遍历wrapper也就是servlet。
                    if (!child.getState().isAvailable()) {
                        child.start();
                    }
                }
                } 
            }
            unbindThread(oldCCL);
        }
        try {
            if (ok) {
                if (!listenerStart()) {  //启动web项目
                }
            }
            if (ok) {
                if (!filterStart()) { //启动filter
                }
            }
            if (ok) {
                loadOnStartup(findChildren()); //启动loadOnStartup大于0的servlet。
            }
            super.threadStart();
        } finally {
        }
    }

关于WebappLoader的父类加载器,是在ContainerBase获取的,而ContainerBaes的父加载器是在Catalina的
digester.addRule(“Server/Service/Engine”, new SetParentClassLoaderRule(parentClassLoader));
中解析Engine的时候设置的。所以其实也就是catalina的父加载器,也就是在Bootstrap.init()中设置的sharedLoader。所以所有的web应用的父加载器都是sharedLoader。

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

在初始化过程中已经bind(),所以这里我们只需要关注startInternal()

public void startInternal() throws Exception {
        if (!running) {
            running = true;
            paused = false;
            if (getExecutor() == null) {
                createExecutor(); //创建一个默认最大200线程的线程池。
            }
            initializeConnectionLatch(); //对于8080默认创建一个200容量的LimitLatch,200是在初始化bind()的时候设置的,可以到上一篇查找
            startAcceptorThreads(); //开启接受请求的线程
            Thread timeoutThread = new Thread(new AsyncTimeout(), //处理请求超时的线程
                    getName() + "-AsyncTimeout");
            timeoutThread.setPriority(threadPriority);
            timeoutThread.setDaemon(true);
            timeoutThread.start();
        }
    }
5.1 AbstractEndpoint.startAcceptorThreads()
protected final void startAcceptorThreads() {
        int count = getAcceptorThreadCount(); //在bind()过程中设置为了1.
        acceptors = new Acceptor[count];
        for (int i = 0; i < count; i++) {
            acceptors[i] = createAcceptor(); //new Acceptor()
            String threadName = getName() + "-Acceptor-" + i;
            acceptors[i].setThreadName(threadName);
            Thread t = new Thread(acceptors[i], threadName);
            t.setPriority(getAcceptorThreadPriority());
            t.setDaemon(getDaemon());
            t.start(); //启动acceptor。
        }
    }

在看下Acceptor具体实现。

protected class Acceptor extends AbstractEndpoint.Acceptor {
        @Override
        public void run() {
            int errorDelay = 0;
            while (running) {
                state = AcceptorState.RUNNING;
                try {
                    countUpOrAwaitConnection();
                    Socket socket = null;
                    try {
                        socket = serverSocketFactory.acceptSocket(serverSocket); //serverSocket.accept()
                    } catch (IOException ioe) {
                    }
                    errorDelay = 0;
                    if (running && !paused && setSocketOptions(socket)) {
                        if (!processSocket(socket)) { //处理请求
                            countDownConnection();
                            closeSocket(socket);
                        }
                    } else {
                        countDownConnection();
                        closeSocket(socket);
                    }
                }
            state = AcceptorState.ENDED;
        }
    }

总结:
容器的启动过程也是重父容器以此向下,这里也很好的展示了tomcat容器的父子关系。
war包的解压和web项目的部署都在容器的启动过程中实现。
创建了一个默认为1个线程的acceptor在后台监听socket连接。
因为endpoint属性基本都是protocolHandler设置的,在上一章我们讲过protocolHandler可以通过在server.xml对应的Connector节点中指明相应的属性。
所以我们可以指明acceptorThreadCount属性增加我们想要的acceptor数量。

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" acceptorThreadCount="3"/>

我们可以在Host节点中修改appBase,让tomcat实现部署我们放在tomcat的webapp以外的目录里面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值