《深入拆解Tomcat&Jetty》——Tomcat主要组件

对,我就是那个看完一脸懵,回去啃代码的小渣渣

文章目录

      • 1、Tomcat一键启停
        • 1.1、LifeCycle接口
        • 1.2、抽象基类LifeCycleBase
        • 1.3、监听器的注册
        • 1.4、生命周期管理总体类图
      • 2、Tomcat启动流程
        • 2.1、Catalina
        • 2.2、Server组件
        • 2.3、Service组件
        • 2.4、Engine组件

1、Tomcat一键启停

设计就是要找到系统的变化点和不变点。

如果想让一个系统能够对外提供服务,需要创建、组装并启动这些组件;在服务停止的时候,需要释放资源,销毁组件,这是一个动态过程。Tomcat组件之间存在两种关系:

  • 组件有大有小,大组建管理小组件。
  • 组件有内右外,外层组件控制内层组件。

总结来说,就是请求的处理过程是由外层组件来驱动的。

1.1、LifeCycle接口

系统中的不变点是:组件的创建、初始化、启动、状态及状态的转化。
变化点:每个组件的初始化(启动)方法。
将不变点抽象出来,就是LifeCycle接口。

《深入拆解Tomcat&Jetty》——Tomcat主要组件_第1张图片

因为组件的init()和start()调用是由它的父组件的状态变化触发的,可以把组件的生命周期转换成一个个状态,把状态的转变看作一个事件。子组件注册监听父组件的事件,就是观察者模式。
LifeCycle接口里的LifeCycleState定义如下:

public enum LifecycleState {
    NEW(false, null),
    INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
    INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
    STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
    STARTING(true, Lifecycle.START_EVENT),
    STARTED(true, Lifecycle.AFTER_START_EVENT),
    STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
    STOPPING(false, Lifecycle.STOP_EVENT),
    STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
    DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
    DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
    FAILED(false, null);

    private final boolean available;
    private final String lifecycleEvent;

    private LifecycleState(boolean available, String lifecycleEvent) {
        this.available = available;
        this.lifecycleEvent = lifecycleEvent;
    }
    
    public boolean isAvailable() {
        return available;
    }

    public String getLifecycleEvent() {
        return lifecycleEvent;
    }
}

看下Event和State之间的对应关系就比较清楚啦~组件到达相应的状态就触发相应的事件。

1.2、抽象基类LifeCycleBase

LifeCycleBase中对组件的公共逻辑进行了实现,如:

  • 生命状态的转变与维护
  • 生命事件的触发
  • 监听器的添加和删除

为了避免混淆(突出是继承自抽象类),子类需要实现的方法在这里都统一改了名字。

《深入拆解Tomcat&Jetty》——Tomcat主要组件_第2张图片

LifeCycleBase在接口方法init、start、stop、destory实现里调用子类要实现的方法initInternal、startInternal、stopInternal、destroyInternal。以init为例:

@Override
    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());
        }
    }

可以看出状态触发事件是通过setStateInternal来实现的,setStateInternal的实现如下:

    private synchronized void setStateInternal(LifecycleState state, Object data, boolean check)
            throws LifecycleException {
        // 状态检查    
        if (check) {
            ……
        }

		// 事件触发
        this.state = state;
        String lifecycleEvent = state.getLifecycleEvent();
        if (lifecycleEvent != null) {
            fireLifecycleEvent(lifecycleEvent, data);
        }
    }

核心就是fireLifecycleEvent。

protected void fireLifecycleEvent(String type, Object data) {
        LifecycleEvent event = new LifecycleEvent(this, type, data);
        for (LifecycleListener listener : lifecycleListeners) {
            listener.lifecycleEvent(event);
        }
    }

LifeCycleBase中用到的是模板设计模式。

1.3、监听器的注册

LifeCycleBase负责触发事件,并调用监听器的方法,监听器的注册分为两种情况:

  • Tomcat自定义的监听器,是父组件在创建子组件的过程中注册到子组件的,
  • 用户在server.xml中定义的监听器。Tomcat在启动时解析server.xml,创建容器并注册到容器组件。

1.4、生命周期管理总体类图

《深入拆解Tomcat&Jetty》——Tomcat主要组件_第3张图片

2、Tomcat启动流程

《深入拆解Tomcat&Jetty》——Tomcat主要组件_第4张图片

流程说明:

  • startup.sh:Tomcat本质上是一个Java程序,startup.sh脚本会启动一个JVM来运行tomcat启动类BootStrap。
  • Bootstrap:初始化Tomcat的类加载器,创建Catalina。
  • Catalina:启动类,通过解析server.xml创建响应的组件,调用Server的start方法。
  • Server:管理Service组件,调用Service的start方法。
  • Service:管理连接器和顶层容器Engine,调用它们的start方法。

2.1、Catalina

Catalina主要是创建server。

public void start() {
        if (getServer() == null) {
            load();
        }

        if (getServer() == null) {
            log.fatal(sm.getString("catalina.noServer"));
            return;
        }

        // Start the new server
        try {
            getServer().start();
        } catch (LifecycleException e) {
            log.fatal(sm.getString("catalina.serverStartFail"), e);
            try {
                getServer().destroy();
            } catch (LifecycleException e1) {
                log.debug("destroy() failed for failed Server ", e1);
            }
            return;
        }

        // Register shutdown hook
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);
        }

        if (await) {
            await();
            stop();
        }
    }

需要关注的就是CatalinaShutdownHook,Runtime.getRuntime().addShutdownHook的作用就是在JVM销毁前执行一个线程,这里也就是CatalinaShutdownHook,主要是用来做清理工作。

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

2.2、Server组件

具体实现类是StandardServer,是用来管理Service的,比较有意思的就是增加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);
    }
}

2.3、Service组件

Service管理的崽崽有点多。看下定义:

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是为了支持热部署,当Web应用的部署发生变化时,Mapper中映射信息也要跟着变化,MapperListener就是一个监听器,监听变化,更新信息到Mapper中。
MapperListener比较简单,就是负责把信息喂给Mapper。事件监听用的还是LifeCycle。Mapper存的是映射信息,信息是从Host、Context、Wrapper里得到的。

《深入拆解Tomcat&Jetty》——Tomcat主要组件_第5张图片

在MapperListener中是有一个连环注册的:registerHostregisterContextprepareWrapperMappingInfo

	@Override
    public void startInternal() throws LifecycleException {

        setState(LifecycleState.STARTING);

        Engine engine = service.getContainer();
        if (engine == null) {
            return;
        }

        findDefaultHost();

        addListeners(engine);

        Container[] conHosts = engine.findChildren();
        for (Container conHost : conHosts) {
            Host host = (Host) conHost;
            if (!LifecycleState.NEW.equals(host.getState())) {
                // Registering the host will register the context and wrappers
                registerHost(host);
            }
        }
    }
    
	private void registerHost(Host host) {
        String[] aliases = host.findAliases();
        mapper.addHost(host.getName(), aliases, host);
        
        for (Container container : host.findChildren()) {
            if (container.getState().isAvailable()) {
                registerContext((Context) container);
            }
        }

        // Default host may have changed
        findDefaultHost();
    }

	private void registerContext(Context context) {
        Host host = (Host)context.getParent();

        WebResourceRoot resources = context.getResources();
        String[] welcomeFiles = context.findWelcomeFiles();
        List wrappers = new ArrayList<>();

        for (Container container : context.findChildren()) {
            prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
        }

        mapper.addContextVersion(host.getName(), host, contextPath,
                context.getWebappVersion(), context, welcomeFiles, resources,
                wrappers);
    }
    

此外,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();
            }
        }
    }
}

2.4、Engine组件

Engine是顶层容器,对应的默认实现类是StandardEngine,继承了ContainerBase,并实现了Engine接口。其子容器是Host,持有一个Host容器的数组。ContainerBase中对子容器的存储和增删改查都提供了实现。
Engine自己要实现的部分:通过Valve实现将请求转发给子容器Host来处理。

每个容器组件都一个PipeLine,PipeLine中有一个基础阀(Basic Valve)

	public StandardEngine() {
        super();
        pipeline.setBasic(new StandardEngineValve());
    }

StandardEngineValve 的定义如下:

final class StandardEngineValve extends ValveBase {
    public final void invoke(Request request, Response response)
      throws IOException, ServletException {
  
      // 拿到请求中的 Host 容器
      Host host = request.getHost();
      if (host == null) {
          return;
      }
 
      // 调用 Host 容器中的 Pipeline 中的第一个 Valve
      host.getPipeline().getFirst().invoke(request, response);
  }
}

注意点:请求中的Host容器是因为,在请求到达Engine之前,Mapper组件已经对请求进行了路由处理,将容器对象保存到了请求对象中。

虽然看老师给的框架图中Mapper的位置,对上面这句话是没有疑异的,但是,好奇心和心虚让我特别想知道Mapper是咋做到的。然后对着代码看了篇详解:深入理解Tomcat(九)MapperListener和Mapper。写的很清楚,当然并不是看完了就学会怎么玩了……

你可能感兴趣的:(05_极客时间阅读笔记,09_Tomcat)