zoukankan      html  css  js  c++  java
  • tomcat系列之六:Tomcat顶层组件

    一,当启动Tomcat后,各个组件都做了什么

    当执行 startup.sh脚本来启动Tomcat后,

    1,Tomcat本质上是一个Java程序,所以 startup.sh脚本会启动一个JVM来运行Tomcat的启动类 Bootstrap

    2,Bootstrap是初始化 Tomcat 的类加载器,并且创建Catalina

    3,Catalina 是一个启动类,它可以解析 server.xml,创建相应的组件,并且调用 Server 的 start 方法。

    4,Server组件用来管理 Service组件,它可以调用Service的start方法

    5,Service组件的职责就是管理连接器和顶层容器Engine,它可以调用连接器 和 Engine的start方法

    二,Catalina

    Catalina的任务就是创建 Server,它通过解析 server.xml ,把在 server.xml 中配置的各个组件创建出来,然后调用Server组件的init方法和start方法,这样Tomcat就启动了。

    public void start() {
        //1. 如果持有的 Server 实例为空,就解析 server.xml 创建出来
        if (getServer() == null) {
            load();
        }
        //2. 如果创建失败,报错退出
        if (getServer() == null) {
            log.fatal(sm.getString("catalina.noServer"));
            return;
        }
    
        //3. 启动 Server
        try {
            getServer().start();
        } catch (LifecycleException e) {
            return;
        }
    
        // 创建并注册关闭钩子
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);
        }
    
        // 用 await 方法监听停止请求
        if (await) {
            await();
            stop();
        }
    }

    那么,Tomcat是如何停止和清理资源的呢?

    首先,Catalina在JVM中注册一个 “关闭钩子”,这个钩子可以在关闭时做一些清理操作,比如将缓存数据刷到磁盘上。这个钩子其实就是一个线程,JVM在停止之前会尝试执行这个线程的run方法。

    CatalinaShutdownHook:

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

    Tomcat的 “关闭钩子” 实际上就执行了 Server的stop方法,Server的stop方法会释放和清理所有的资源。

    三,Server组件

    具体实现类:StandardServer。

    Server的子组件是Service,所以它要管理Service的生命周期。在Server启动时,需要调用Service组件的启动方法,在停止时调用它们的停止方法。Server在内部维护了Service组件,通过数组来保存。Server如何添加一个Service到数组中?

    @Override
    public void addService(Service service) {
    
        service.setServer(this);
    
        synchronized (servicesLock) {
            // 创建一个长度 +1 的新数组
            Service results[] = new Service[services.length + 1];
            
            // 将老的数据复制过去
            System.arraycopy(services, 0, results, 0, services.length);
            results[services.length] = service;
            services = results;
    
            // 启动 Service 组件
            if (getState().isAvailable()) {
                try {
                    service.start();
                } catch (LifecycleException e) {
                    // Ignore
                }
            }
    
            // 触发监听事件
            support.firePropertyChange("service", null, service);
        }
    
    }

    四,Service组件

    Service组件的具体实现类:StandardService。

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

    可以看到StandardService中的组件有:Connector,Engine,Mapper,MapperListener(这是一个监听器,当Web应用的部署发生变化时,把信息更新到Mapper中,热部署时用到)。

    StandardService中的启动方法:

    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组件

    具体实现类:StandardEngine,Engine本质是一个容器,它继承了ContainerBase基类,并实现Engine接口

    public class StandardEngine extends ContainerBase implements Engine {
    }

    Engine的子容器是Host,它持有一个Host容器的数组,

    CatalinaShutdownHook

    极客时间版权所有: https://time.geekbang.org/column/article/97603:

  • 相关阅读:
    龙果支付系统
    Java并发多线程
    StringRedisTemplate常用操作
    统一支付平台转型
    IntValue()方法 和 ValueOf()方法
    Java中一些知识的归纳总结
    mybatis的一些特殊符号标识(大于,小于,等于,不等于)
    MySQL中大于等于小于等于的写法
    boost::bind应用示例
    VC除零异常(错误)捕获
  • 原文地址:https://www.cnblogs.com/inspred/p/10979366.html
Copyright © 2011-2022 走看看