Tomcat的生命周期管理

简介: Tomcat的生命周期管理
  • Tomcat组件之间的静态关系

1.png

虚线表示一个请求在Tomcat中的流转


若想让一个系统能对外提供服务,需创建、组装并启动这些组件;在服务停止时,还需要释放资源,这是一个动态过程。即Tomcat需动态管理这些组件的生命周期。


当我们设计一个较大系统或框架时,也需要考虑:


  • 如何统一管理组件的创建、初始化、启动、停止和销毁?
  • 如何做到代码逻辑清晰?
  • 如何方便地添加或者删除组件?
  • 如何做到组件启动和停止不遗漏、不重复?


组件有大有小,大组件管理小组件,比如Server管理Service,Service又管理连接器和容器。

组件有外有内,外层组件控制内层组件,比如连接器是外层组件,负责对外交流,外层组件调用内层组件完成业务功能。即请求的处理过程由外层组件驱动。


这两层关系决定了系统在创建组件时应该遵循一定的顺序:


  • 先创建子组件,再父组件,子组件需要被“注入”到父组件中
  • 先创建内组件,再外组件,内组件要被“注入”到外组件


因此,最直观的做法就是将图上所有的组件按照先小后大、先内后外的顺序创建,然后组装。

这个思路其实很有问题:

  • 代码逻辑混乱、组件遗漏
  • 不利于后期的功能扩展


为了解决这个问题,我们希望找到一种通用的、统一的方法来管理组件的生命周期,就像汽车“一键启动”那样的效果。

一键式启停:Lifecycle接口

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

  • 不变点
    每个组件都要经历创建、初始化、启动这几个过程,这些状态以及状态的转化是不变的
  • 变化点
    每个具体组件的初始化方法,即启动方法不同


把不变点抽象出来成为一个接口,这个接口跟生命周期有关,叫Lifecycle。Lifecycle接口里应该定义这么几个方法:init、start、stop和destroy,每个具体的组件去实现这些方法。


父组件的init方法里需要创建子组件并调用子组件的init方法。同理父组件的start调用子组件的start,因此调用者可以无差调用各组件的init、start,这就是组合模式,并且只要调用最顶层组件,也就是Server组件的init和start方法,整个Tomcat就被启动起来了。


  • Lifecycle接口定义

image.png

可扩展性-Lifecycle事件

在Host容器的启动方法需扫描webapps目录下的Web应用,创建相应Context容器,若以后新增逻辑,直接修改start方法?这会违反开闭原则,那如何解决这个问题呢?


组件的init和start调用是由它的父组件的状态变化触发的,上层组件的初始化会触发子组件的初始化,上层组件的启动会触发子组件的启动,因此我们把组件的生命周期定义成一个个状态,把状态的转变看作是一个事件。

而事件有监听器,在监听器里可以实现一些逻辑。


于是可以在Lifecycle接口里加入两个方法:添加监听器和删除监听器。还需要定义一个Enum表示组件有哪些状态,以及处在什么状态会触发什么样的事件。

因此Lifecycle接口和LifecycleState就定义成了:

image.png

组件的生命周期有NEW、INITIALIZING、INITIALIZED、STARTING_PREP、STARTING、STARTED等,一旦组件到达相应的状态就触发相应的事件,比如


  • NEW状态表示组件刚刚被实例化
  • 当init方法被调用,状态变成INITIALIZING
  • 这时,就会触发BEFORE_INIT_EVENT事件,若有监听器在监听该事件,它的方法就会被调用

重用性-LifecycleBase抽象基类

有了接口,就要用类去实现接口。不同类在实现接口时往往会有一些相同的逻辑,如果让各个子类都去实现一遍,就会有重复代码。那子类如何重用这部分逻辑呢?

定义基类实现共同逻辑,然后让各个子类去继承它,达到重用。


而基类中往往会定义一些抽象方法,基类不会去实现这些方法,而是调用这些方法来实现骨架逻辑。留给子类实现,并且子类必须实现,否则无法实例化。


Tomcat定义一个基类LifecycleBase来实现Lifecycle接口,把一些公共逻辑放到该基类,比如

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


子类就负责实现自己的初始化、启动和停止等方法。为了避免跟基类中的方法同名,我们把具体子类的实现方法改个名字,在后面加上Internal,叫initInternal、startInternal等。

  • 引入基类LifecycleBase后的类图

image.png

LifecycleBase定义了相应的抽象方法交给具体子类去实现,这是模板设计模式。

LifecycleBase#init

@Override
public final synchronized void init() throws LifecycleException {
    // 1. 状态检查
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
    try {
        // 2.触发INITIALIZING事件的监听器
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        // 3.调用具体子类的初始化方法
        initInternal();
        // 4. 触发INITIALIZED事件的监听器
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
      ...
    }
}

这个方法逻辑比较清楚,主要完成了四步:

  • 检查状态的合法性,比如当前状态必须是NEW然后才能进行初始化
  • 触发INITIALIZING事件的监听器:
    setStateInternal(LifecycleState.INITIALIZING, null, false);
    在这个setStateInternal方法里,会调用监听器的业务方法。
  • 调用具体子类实现的抽象方法initInternal方法。我在前面提到过,为了实现一键式启动,具体组件在实现initInternal方法时,又会调用它的子组件的init方法
  • 子组件初始化后,触发INITIALIZED事件的监听器,相应监听器的业务方法就会被调用
    setStateInternal(LifecycleState.INITIALIZED, null, false);


LifecycleBase负责触发事件,并调用监听器的方法,那是什么时候、谁把监听器注册进来的呢?分为两种情况:


  • Tomcat自定义了一些监听器,这些监听器是父组件在创建子组件的过程中注册到子组件的。比如MemoryLeakTrackingListener监听器,用来检测Context容器中的内存泄漏,这个监听器是Host容器在创建Context容器时注册到Context中的
  • 还可以在server.xml中定义自己的监听器,Tomcat在启动时会解析server.xml,创建监听器并注册到容器组件。

生周期管理总体类图

StandardServer、StandardService等是Server和Service组件的具体实现类,它们都继承了LifecycleBase。

1.png

StandardEngine、StandardHost、StandardContext和StandardWrapper是相应容器组件的具体实现类,因为它们都是容器,所以继承了ContainerBase抽象基类,而ContainerBase实现了Container接口,也继承了LifecycleBase类,它们的生命周期管理接口和功能接口是分开的,这也符合设计中接口分离的原则。


  • 如果你需要维护一堆具有父子关系的实体,可考虑组合模式
  • 观察者模式

当一个事件发生后,需要执行一连串更新操作。传统的实现方式是在事件响应代码里直接加更新逻辑,当更新逻辑加多了之后,代码会变得臃肿,并且这种方式是紧耦合的、侵入式的。而观察者模式实现了低耦合、非侵入式的通知与更新机制。

  • 模板方法在抽象基类中经常用到,用来实现通用逻辑。


目录
相关文章
|
存储 应用服务中间件 容器
Tomcat-Tomcat生命周期
Tomcat-Tomcat生命周期
Tomcat-Tomcat生命周期
|
Java 应用服务中间件
面试官:既然启动流程不太了解,那你知道Tomcat的生命周期是什么样子的么?
上一次的文章中,阿粉在面试官面前说了对启动流程不太理解,然后和他聊了一会,然后他又提出了你既然不是特别了解启动流程的话,那你对Tomcat的生命周期熟悉么?
面试官:既然启动流程不太了解,那你知道Tomcat的生命周期是什么样子的么?
|
设计模式 前端开发 应用服务中间件
拆解Tomcat10: (六) 核心组件的生命周期管理与组合模式解析(二)
上一篇《拆解Tomcat10 (五) 核心组件的初始化与设计模式解析》分享了Tomcat的核心组件的是如何实现生命周期接口的,以及生命周期方法的调用逻辑。如果把Tomcat比作一台机器,那么这台机器是如何开机、关机的呢? 当按下开机键,所有核心组件会逐步初始化、启动;当按下关机键,所有组件又会随着关机,释放资源,这是如何实现的呢?
934 0
拆解Tomcat10: (六) 核心组件的生命周期管理与组合模式解析(二)
|
Java 应用服务中间件 容器
|
Java 应用服务中间件 容器
Tomcat7.0源码分析——生命周期管理
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/51473807 前言  从server.xml文件解析出来的各个对象都是容器,比如:Server、Service、Connector等。
980 0
|
Java 应用服务中间件 容器
TOMCAT源码分析——生命周期管理(一)
前言   从server.xml文件解析出来的各个对象都是容器,比如:Server、Service、Connector等。这些容器都具有新建、初始化完成、启动、停止、失败、销毁等状态。tomcat的实现提供了对这些容器的生命周期管理,本文将通过对Tomcat7.0的源码阅读,深入剖析这一过程。
4432 0
|
应用服务中间件 容器
tomcat生命周期的管理——生命周期统一接口Lifecycle
我们知道Tomcat的架构设计是清晰的、模块化的,其拥有很多组件,假如我们要启动Tomcat,可以一个一个启动组件,但这样启动有很多缺点,不仅麻烦,而且容易漏了组件启动,还会对后面动态组件扩展带来麻烦。
876 0
|
2天前
|
XML Java 应用服务中间件
Tomcat_servlet部署、编译、配置、打包
Tomcat_servlet部署、编译、配置、打包
23 0
|
2天前
|
IDE Java 应用服务中间件
JDK1.6.0+Tomcat6.0的安装配置(配置JAVA环境)
JDK1.6.0+Tomcat6.0的安装配置(配置JAVA环境)
18 1