美文网首页
执行startup.sh后tomcat各层都做了什么?

执行startup.sh后tomcat各层都做了什么?

作者: renmen2000 | 来源:发表于2020-06-18 17:46 被阅读0次

    概述

    启动tomcat的时候一般都会执行bin目录下的startup.sh或者startup.bat(Windows系统下),执行此脚本后具体是怎么启动tomcat的呢?请看下图


    20200618-1.png
    • 执行tomcat启动类Bootstrap
      • 初始化tomcat的类加载器(tomcat实现了自己的类加载器来加载类,需要单独说明)
      • 创建Catalina
    • Catalina是tomcat的启动类,用于解析server.xml并创建server组件,并调用其start方法
    • Server组件管理service组件并调用其start方法
    • Service组件管理Engine和各个连接器组件,并调用他们的start方法
      这样就完成了整个tomcat的启动。
      Catalina
     public void start() {
            // 如果server为空,读取server.xml创建server
            if (this.getServer() == null) {
                this.load();
            }
            // 此时说明创建server失败,则报错退出
            if (this.getServer() == null) {
                log.fatal("Cannot start server. Server instance is not configured.");
            } else {
                long t1 = System.nanoTime();
                // 启动server
                try {
                    this.getServer().start();
                } catch (LifecycleException var7) {
                    log.fatal(sm.getString("catalina.serverStartFail"), var7);
    
                    try {
                        this.getServer().destroy();
                    } catch (LifecycleException var6) {
                        log.debug("destroy() failed for failed Server ", var6);
                    }
    
                    return;
                }
    
                long t2 = System.nanoTime();
                if (log.isInfoEnabled()) {
                    log.info("Server startup in " + (t2 - t1) / 1000000L + " ms");
                }
                // 创建并注册关闭钩子
                if (this.useShutdownHook) {
                    if (this.shutdownHook == null) {
                        this.shutdownHook = new Catalina.CatalinaShutdownHook();
                    }
    
                    Runtime.getRuntime().addShutdownHook(this.shutdownHook);
                    LogManager logManager = LogManager.getLogManager();
                    if (logManager instanceof ClassLoaderLogManager) {
                        ((ClassLoaderLogManager)logManager).setUseShutdownHook(false);
                    }
                }
                // 监听关闭请求
                if (this.await) {
                    this.await();
                    this.stop();
                }
    
            }
        }
    

    catalina的start方法如上,参考注释部分。

    关闭钩子的作用是什么呢?

    为了应对异常关闭的情况,比如执行“Ctrl + C”关闭,此时出发钩子线程执行必要的清理工作,实现优雅关闭。

    什么是关闭钩子呢?

    其实就是一个线程,jvm在关闭之前,会调用该线程的run方法

    tomcat的关闭钩子干了啥?

    protected class CatalinaShutdownHook extends Thread {
    
        @Override
        public void run() {
            try {
                if (getServer() != null) {
                    Catalina.this.stop();
                }
            } catch (Throwable ex) {
               ...
            }
        }
    }
    

    实际上就是调了Catalina的stop方法,stop方法会释放和清理所有资源

    Server组件

    具体实现类是StandardServer,Server继承了LifecycleBase接口,子组件为Service,所以还需要管理Service的生命周期。

        public void addService(Service service) {
            service.setServer(this);
            synchronized(this.servicesLock) {
                //创建长度为原数组长度+1的数组
                Service[] results = new Service[this.services.length + 1];
                //复制数据到新数组
                System.arraycopy(this.services, 0, results, 0, this.services.length);
                //设置数组的最后一个元素为当前service
                results[this.services.length] = service;
                this.services = results;
                // 启动service组件
                if (this.getState().isAvailable()) {
                    try {
                        service.start();
                    } catch (LifecycleException var6) {
                    }
                }
                //触发监听事件
                this.support.firePropertyChange("service", (Object)null, service);
            }
        }
    
    • 代码中对于service数据长度的分配,每次创建时数组长度加1动态分配,不会一开始就分配很大,节约内存
    • server组件有个功能就是启动一个Socket监听停止端口,这也是可以使用shutdown命令关闭tomcat的原因
    • Catalina的start方法里this.await();一行,最终调用的是server的await()方法
      • server的await()方法会创建一个Socket监听8005端口
      • 如果该端口有连接到来就建立连接读取数据,如果读到的是SHUTDOWN命令就进入stop流程
        Service组件

    Service 组件的具体实现类是 StandardService,StandardService 继承了 LifecycleBase 抽象类,看看其成员变量

    public class StandardService extends LifecycleBase implements Service {
        //名字
        private String name = null;
        //Server实例
        private Server server = null;
        //连接器数组
        protected Connector connectors[] = new Connector[0];
        private final Object connectorsLock = new Object();
        //对应的Engine容器
        private Engine engine = null;
      
        //映射器及其监听器
        protected final Mapper mapper = new Mapper();
        protected final MapperListener mapperListener = new MapperListener(this);
    

    MapperListener是干什么的呢?

    tomcat支持热部署,当web应用发生变化时,Mapper中的配置信息也需要跟着变化

    MapperListener实际就是一个监听器,监听容器变化,实时更新信息到Mapper中

    这里是典型的观察者模式的使用

    service如果管理其他组件?

    protected void startInternal() throws LifecycleException {
    
        //1. 触发启动监听器
        setState(LifecycleState.STARTING);
    
        //2. 先启动Engine,Engine会启动它子容器
        if (engine != null) {
            synchronized (engine) {
                engine.start();
            }
        }
        
        //3. 再启动Mapper监听器
        mapperListener.start();
    
        //4.最后启动连接器,连接器会启动它子组件,比如Endpoint
        synchronized (connectorsLock) {
            for (Connector connector: connectors) {
                if (connector.getState() != LifecycleState.FAILED) {
                    connector.start();
                }
            }
        }
    }
    

    看顺序是现启动Engine再启动Mapper监听器,最后启动连接器组件

    原因是先启动内部组件并监听,然后再启动外部组件对外提供服务

    Engine组件

    • engine本身是个容器,继承了 ContainerBase 基类,并且实现了 Engine 接口
    • engine的子容器是host,它持有一个Host容器数组
    protected final HashMap<String, Container> children = new HashMap<>();
    

    对于容器的启停管理,增删改查等功能都抽象到了ContainerBase基础类中,注意ContainerBase 会用专门的线程池来启动子容器。

    for (int i = 0; i < children.length; i++) {
       results.add(startStopExecutor.submit(new StartChild(children[i])));
    }
    
    • 所以Engine启动host时直接使用了该方法
    • Engine做了什么事呢?其实就是把请求转发给某个Host来处理
    • Engine如何知道请求该转发到哪个Host呢?请求到达 Engine 容器中之前,Mapper 组件已经对请求进行了路由处理,Mapper 组件通过请求的 URL 定位了相应的容器,并且把容器对象保存到了请求对象中。

    相关文章

      网友评论

          本文标题:执行startup.sh后tomcat各层都做了什么?

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