美文网首页
揭开tomcat神秘的面纱之bootstrap启动1

揭开tomcat神秘的面纱之bootstrap启动1

作者: 一滴水的坚持 | 来源:发表于2018-11-12 00:02 被阅读0次

    在上文揭开tomcat神秘的面纱之bootstrap加载中,本菜鸟分析了bootstrap最终会经过初始化,加载,启动三个步骤。接着来分析启动过程。

    tomcat的启动过程.png
    bootstrapmain方法中,加载完成之后就会调用catalinastart方法,启动容器,而在catalinastart方法中,实质是调用serverstart方法。
    //Bootstrap.java
    public void start()
        throws Exception {
        if( catalinaDaemon==null ) init();
        Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
        method.invoke(catalinaDaemon, (Object [])null);//catalina的start方法。
    }   
    Catalina.java
     public void start() {
        if (getServer() == null) {
            load();
        }
    
        if (getServer() == null) {
            return;
        }
        getServer().start();//拿到的是StandardServer
    }
    

    而在StandardServerstart方法中,实质是调用StandServicestart方法,最终调用enginestart方法。

    //StandardServer.java
    protected void startInternal() throws LifecycleException {
        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);
        globalNamingResources.start();
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();//这里调用StandService的start方法。
            }
        }
    }
    //StandardService.java
    @Override
    protected void startInternal() throws LifecycleException {
    
        setState(LifecycleState.STARTING);
        if (engine != null) {
            synchronized (engine) {
                engine.start();
            }
        }
        synchronized (executors) {
            for (Executor executor: executors) {
                executor.start();
            }
        }
        mapperListener.start();
        synchronized (connectorsLock) {
            for (Connector connector: connectors) {
                if (connector.getState() != LifecycleState.FAILED) {
                    connector.start();
                }
            }
        }
    }
    

    关于这里为啥不是start方法,而是startInternal,这是因为他们共同继承于LifecycleBase类,该类中,会调用startInternal方法,调用start方法,实质最终都会调用startInternal

    //LifecycleBase.java
    public final synchronized void start() throws LifecycleException {
        if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
                LifecycleState.STARTED.equals(state)) {
            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);
        startInternal();//子类实现该接口
        if (state.equals(LifecycleState.FAILED)) {
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
          
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
       
    }
    

    而在StandardEnginestart方法中,实质是通过多线程,创建StartChild方法,启动StandardHost,

    //ContainerBase.java
     protected synchronized void startInternal() throws LifecycleException {
    
        Cluster cluster = getClusterInternal();
        if (cluster instanceof Lifecycle) {
            ((Lifecycle) cluster).start();
        }
        Realm realm = getRealmInternal();
        if (realm instanceof Lifecycle) {
            ((Lifecycle) realm).start();
        }
    
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();
        for (int i = 0; i < children.length; i++) {
            results.add(startStopExecutor.submit(new StartChild(children[i])));
            //这里提交任务,就是启动StandardHost
        }
        MultiThrowable multiThrowable = null;
        for (Future<Void> result : results) {
                result.get();
        }
        if (pipeline instanceof Lifecycle) {
            ((Lifecycle) pipeline).start();
        }
        setState(LifecycleState.STARTING);
        threadStart();
    }
    

    而在StartChild方法中,只是调用子容器的start方法。此处为StandardHost

    StartChild.java
    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;
        }
    }
    

    而在StandardHost调用start方法的时候,会广播一个start事件,最终会被HostConfig接收,并处理,调用HostConfigstart方法,最终会将webapp文件夹下的每一个war,或者文件夹,单独作为一个任务DeployWar,提交到线程池,去启动这个Context,而DeployWar这个任务,实质还是调用HostConfigdeployWAR方法,这里本菜鸟就以一个war包为例,来介绍一下。

      public void start() {
        ObjectName hostON = host.getObjectName();
        oname = new ObjectName
            (hostON.getDomain() + ":type=Deployer,host=" + host.getName());
        Registry.getRegistry(null, null).registerComponent
            (this, oname, this.getClass().getName());
        if (!host.getAppBaseFile().isDirectory()) {
            host.setDeployOnStartup(false);
            host.setAutoDeploy(false);
        }
        if (host.getDeployOnStartup())
            deployApps();//发布app
    }
    
     protected void deployApps() {
        File appBase = host.getAppBaseFile();
        File configBase = host.getConfigBaseFile();
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        // Deploy XML descriptors from configBase
        deployDescriptors(configBase, configBase.list());//发布描述配置信息中的服务
        // Deploy WARs
        deployWARs(appBase, filteredAppPaths);//发布war包服务
        // Deploy expanded folders
        deployDirectories(appBase, filteredAppPaths);//发布文件夹的包服务
    }
    
    protected void deployWARs(File appBase, String[] files) {
        ExecutorService es = host.getStartStopExecutor();
        List<Future<?>> results = new ArrayList<>();
        for (int i = 0; i < files.length; i++) {
            if (files[i].equalsIgnoreCase("META-INF"))
                continue;
            if (files[i].equalsIgnoreCase("WEB-INF"))
                continue;
            File war = new File(appBase, files[i]);
            if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") &&
                    war.isFile() && !invalidWars.contains(files[i]) ) {
                ContextName cn = new ContextName(files[i], true);
                if (isServiced(cn.getName())) {
                    continue;
                }
                if (deploymentExists(cn.getName())) {
                    DeployedApplication app = deployed.get(cn.getName());
                    boolean unpackWAR = unpackWARs;
                    if (unpackWAR && host.findChild(cn.getName()) instanceof StandardContext) {
                        unpackWAR = ((StandardContext) host.findChild(cn.getName())).getUnpackWAR();
                    }
                    if (!unpackWAR && app != null) {
                        File dir = new File(appBase, cn.getBaseName());
                        if (dir.exists()) {
                                app.loggedDirWarning = true;
                        } else {
                            app.loggedDirWarning = false;
                        }
                    }
                    continue;
                }
    
                if (!validateContextPath(appBase, cn.getBaseName())) {
                    invalidWars.add(files[i]);
                    continue;
                }
                results.add(es.submit(new DeployWar(this, cn, war)));
                //重点在这里,提交一个DeployWar任务。
            }
        }
        for (Future<?> result : results) {
                result.get();
        }
    }
    
    private static class DeployWar implements Runnable {
        private HostConfig config;
        private ContextName cn;
        private File war;
        public DeployWar(HostConfig config, ContextName cn, File war) {
            this.config = config;
            this.cn = cn;
            this.war = war;
        }
        @Override
        public void run() {
            config.deployWAR(cn, war);
            //HostConfig的deployWAR方法。
        }
    }
    

    而在HostConfigdeployWAR方法中,会创建Context,然后将该Context添加到Host过程中(addChild方法),会启动Context

    
    protected void deployWAR(ContextName cn, File war) {
    
        File xml = new File(host.getAppBaseFile(),
                cn.getBaseName() + "/" + Constants.ApplicationContextXml);
    
        File warTracker = new File(host.getAppBaseFile(), cn.getBaseName() + Constants.WarTracker);
    
        boolean xmlInWar = false;
        try (JarFile jar = new JarFile(war)) {
            JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
            if (entry != null) {
                xmlInWar = true;
            }
        } catch (IOException e) {
            /* Ignore */
        }
    
       
        boolean useXml = false;
        if (xml.exists() && unpackWARs &&
                (!warTracker.exists() || warTracker.lastModified() == war.lastModified())) {
            useXml = true;
        }
    
        Context context = null;
        //初始化Context
        boolean deployThisXML = isDeployThisXML(war, cn);
        if (deployThisXML && useXml && !copyXML) {
            synchronized (digesterLock) {
                context = (Context) digester.parse(xml);
               
            }
            context.setConfigFile(xml.toURI().toURL());
        } else if (deployThisXML && xmlInWar) {
            synchronized (digesterLock) {
                try (JarFile jar = new JarFile(war)) {
                    JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
                    context = (Context) digester.parse(istream);
            }
        } else if (!deployThisXML && xmlInWar) {
        } else {
            context = (Context) Class.forName(contextClass).getConstructor().newInstance();
        }
        
        boolean copyThisXml = false;
        if (deployThisXML) {
            if (host instanceof StandardHost) {
                copyThisXml = ((StandardHost) host).isCopyXML();
            }
            if (!copyThisXml && context instanceof StandardContext) {
                copyThisXml = ((StandardContext) context).getCopyXML();
            }
    
            if (xmlInWar && copyThisXml) {
                xml = new File(host.getConfigBaseFile(),
                        cn.getBaseName() + ".xml");
                try (JarFile jar = new JarFile(war)) {
                    JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml);
                    try (InputStream istream = jar.getInputStream(entry);
                            FileOutputStream fos = new FileOutputStream(xml);
                            BufferedOutputStream ostream = new BufferedOutputStream(fos, 1024)) {
                        byte buffer[] = new byte[1024];
                        while (true) {
                            int n = istream.read(buffer);
                            if (n < 0) {
                                break;
                            }
                            ostream.write(buffer, 0, n);
                        }
                        ostream.flush();
                    }
                } catch (IOException e) {
                }
            }
        }
    
        DeployedApplication deployedApp = new DeployedApplication(cn.getName(),
                xml.exists() && deployThisXML && copyThisXml);
    
        long startTime = 0;
        try{
            deployedApp.redeployResources.put
                (war.getAbsolutePath(), Long.valueOf(war.lastModified()));
            if (deployThisXML && xml.exists() && copyThisXml) {
                deployedApp.redeployResources.put(xml.getAbsolutePath(),
                        Long.valueOf(xml.lastModified()));
            } else {
                deployedApp.redeployResources.put(
                        (new File(host.getConfigBaseFile(),
                                cn.getBaseName() + ".xml")).getAbsolutePath(),
                        Long.valueOf(0));
            }
      
            Class<?> clazz = Class.forName(host.getConfigClass());
            LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
            context.addLifecycleListener(listener);
            context.setName(cn.getName());
            context.setPath(cn.getPath());
            context.setWebappVersion(cn.getVersion());
            context.setDocBase(cn.getBaseName() + ".war");
            host.addChild(context);//添加子Context
        } catch (Throwable t) {
           
        } finally {
            boolean unpackWAR = unpackWARs;
            if (unpackWAR && context instanceof StandardContext) {
                unpackWAR = ((StandardContext) context).getUnpackWAR();
            }
            if (unpackWAR && context.getDocBase() != null) {
                File docBase = new File(host.getAppBaseFile(), cn.getBaseName());
              deployedApp.redeployResources.put(docBase.getAbsolutePath(),
                        Long.valueOf(docBase.lastModified()));
                addWatchedResources(deployedApp, docBase.getAbsolutePath(),
                        context);
                if (deployThisXML && !copyThisXml && (xmlInWar || xml.exists())) {
                    deployedApp.redeployResources.put(xml.getAbsolutePath(),
                            Long.valueOf(xml.lastModified()));
                }
            } else {
                addWatchedResources(deployedApp, null, context);
            }
            addGlobalRedeployResources(deployedApp);
        }
        deployed.put(cn.getName(), deployedApp);
    }
    
       private void addChildInternal(Container child) {
    
            if( log.isDebugEnabled() )
                log.debug("Add child " + child + " " + this);
            synchronized(children) {
                if (children.get(child.getName()) != null)
                    throw new IllegalArgumentException("addChild:  Child name '" +
                                                       child.getName() +
                                                       "' is not unique");
                child.setParent(this);  // May throw IAE
                children.put(child.getName(), child);
            }
    
            // Start child
            // Don't do this inside sync block - start can be a slow process and
            // locking the children object can cause problems elsewhere
            try {
                if ((getState().isAvailable() ||
                        LifecycleState.STARTING_PREP.equals(getState())) &&
                        startChildren) {
                    child.start();//启动Context
                }
            } catch (LifecycleException e) {
                log.error("ContainerBase.addChild: start: ", e);
                throw new IllegalStateException("ContainerBase.addChild: start: " + e);
            } finally {
                fireContainerEvent(ADD_CHILD_EVENT, child);
            }
        }
    //ContainerBase.java 此处为StandardHost.addChild方法
    //addChild
    private void addChildInternal(Container child) {
        synchronized(children) {
            child.setParent(this);  
            children.put(child.getName(), child);
        }
        try {
            if ((getState().isAvailable() ||
                    LifecycleState.STARTING_PREP.equals(getState())) &&
                    startChildren) {
                child.start();//启动context
            }
        } finally {
            fireContainerEvent(ADD_CHILD_EVENT, child);
        }
    }
    

    关于Context初始化的内容,后面继续更新。

    相关文章

      网友评论

          本文标题:揭开tomcat神秘的面纱之bootstrap启动1

          本文链接:https://www.haomeiwen.com/subject/umvffqtx.html