• Tomcat生命周期管理与观察者模式


    本文主要结合观察者模式,讲述Tomcat的生命周期管理。Tomcat的生命周期管理机制设计的非常优雅,在Tomcat启动时,只需要启动一个Server组件,就会启动所有的容器及对应的组件,并且触发这些容器的监听者,完成启动过程的设置。可以说是“一键式”启动的。停止过程也是一样。

             本文首先简单介绍Tomcat中容器,组件及监听类的功能。因为Tomcat的生命周期管理应用了观察者模式,所以接下来会分析观察者模式,最后结合观察者模式及Tomcat源代码,详细说明Tomcat的生命周期管理。

    一、几种基本的容器,组件及事件监听类(Listener)

    1.         Tomcat中有四种不同的容器:

    • Engine:代表整个Catalina servle引擎
    • Host:代表虚拟主机
    • Context:代表某个web应用
    • Wrapper:代表某个应用中的servlet

             这些容器都扩展了Container接口(译为:容器,这也是为什么一般都称tomcat为容器而不是服务器的原因之一吧~)。更重要的是,这些容器都是父子的关系,Engine位于最顶层,一个Engine包含多个Host,一个Host(虚拟主机)包含多个Context(web应用),一个Context(web 应用)包含多个Wrapper(servlet),Wrapper位于最底层,没有孩子。当父容器启动时,相应的子容器也应该启动,子容器的子容器也启动。如此,只需要启动最顶层的容器,就会启动所有的子容器。

     

    2.         Tomcat的容器中有很多组件,如:

    • Logger:负责记录各种事件
    • Loader:负责加载类文件,如加载应用程序中的Servlet
    • Manager:负责管理session
    • Realm: 负责用户验证与授权
    • Pipeline:负责完成容器invoke方法的调用,对请求进行处理(责任链模式的经典应用)。

             当tomcat容器启动时,这些组件也要启动,进行初始化。当容器停止时,这些组件也应该停止,进行相应的清理工作。比如管理用户session的Manager组件,在tomcat停止时,会将这些session序列化到sessions.ser文件中(位于%tomcat_home%/work/web appcation/sessions.ser)。下一次启动tomcat时,manager会将这个文件中的session反序列化到内存,将删除sessions.ser文件。这也是为什么重启tomcat时,正在访问网站的用户不用重新登录的原因。

     

    3.         另外,还有一些类,它们并不像容器的基本组件(如Logger, Loader, Manager)一样,为容器的整个生命周期所调用,而仅仅对容器的某几个特定事件感兴趣。如:

    • ContextConfig: Context的收听者,在Context(web 应用)启动时,ContextConfig对web应用程序的配置文件web.xml进行分析,为Context生成Wrapper等对象,并与Context关联。在Context停止时,为Context清除这些关联的对象。
    • HostConfig: Host的收听者,在Host(虚拟主机)启动时,HostConfig会自动的为Host部署放置在webapps中的web应用程序。在Host停止时,为Host清除这些关联的对象。
    • EngineConfig:Engine的收听者,这个对比较简单,仅仅是在Engine启动与停止时做一些简单的记录。

             这些监听类如何监听容器的特定事件呢?如何在特定事件发现时,调用监听类的特定方法以完成某些设置呢?如果理解了观察者模式,便能轻易的理解Tomcat的整个生命周期管理了。

     

    二、观察者模式:

             观察者模式又叫做发布-订阅(Publish/Subscribe)模式、源-监听(Source/Listener)模式。它定义了一种一对多的依赖关系,一个主题,多个观察者,当主题发生变化的时候,会主动的通知观察者,这样观察者便能针对主题发生的变化,执行某些对应的动作。观察者模式的应用非常广泛,如Java AWT事件模型,Servlet的监听器,Spring事件处理机制以及本文所讲述的Tomcat生命周期管理机制等等;应用如此的广泛,以至于Java直接提供API的支持,java.util.Observable和java.util.Observer;

    观察者模式的结构:

     观察者模式

     

     观察者模式包括以下角色:

    抽象主题(Observable):定义了管理观察者的添加,删除和通知方法。

    抽象观察者(Observer):定义了主题发生变化时,具体观察者必须执行的方法。

    具体主题(Container):对观察者进行管理,并在自身发生变化时,通知观察者。

    具体观察者(ContainerConfig):实现了当主题发生变化时,应该执行的动作。

     

    下面是观察者模式的示例代码:

    package com.scnulh.observer;

    抽象主题:

    Java代码  收藏代码
    1. /** 
    2.  
    3.  * 抽象主题 
    4.  
    5.  */  
    6.   
    7. public interface Observable {  
    8.   
    9.       
    10.   
    11.     //添加观察者  
    12.   
    13.     void addObserver(Observer observer);  
    14.   
    15.     //删除观察者  
    16.   
    17.     void deleteObserver(Observer observer);  
    18.   
    19.     //在事件发生时,通知观察者  
    20.   
    21.     void notifyObservers();  
    22.   
    23. }  

     

     

    抽象观察者:

    Java代码  收藏代码
    1. package com.scnulh.observer;  
    2.   
    3.    
    4.   
    5. /** 
    6.  
    7.  * 抽象观察者 
    8.  
    9.  */  
    10.   
    11. public interface Observer {  
    12.   
    13.       
    14.   
    15.     /** 
    16.  
    17.      * 更新方法,观察者根据传入的主题对象获取主题的上下文 
    18.  
    19.      * 根据传入的Object对象判断发生了何种事件 
    20.  
    21.      * @param observable 
    22.  
    23.      * @param arg 
    24.  
    25.      */  
    26.   
    27.     void update(Observable observable,Object arg);  
    28.   
    29.    
    30.   
    31. }  

     

     

     

    具体主题:

    Java代码  收藏代码
    1. package com.scnulh.observer;  
    2.   
    3.    
    4.   
    5. import java.util.ArrayList;  
    6.   
    7. import java.util.List;  
    8.   
    9.    
    10.   
    11. /** 
    12.  
    13.  * 具体主题,假设这是一个Tomcat的容器基类 
    14.  
    15.  * 有一个start方法,代表容器的启动,在启动过程中 
    16.  
    17.  * 通知所有观察者  
    18.  
    19.  */  
    20.   
    21. public class ContainerBase implements Observable{  
    22.   
    23.    
    24.   
    25.     //持有观察者的List  
    26.   
    27.     private List<Observer> observers=new ArrayList<Observer>();   
    28.   
    29.    
    30.   
    31.    
    32.   
    33.     //添加观察者  
    34.   
    35.     @Override  
    36.   
    37.     public void addObserver(Observer observer) {  
    38.   
    39.        observers.add(observer);  
    40.   
    41.     }  
    42.   
    43.    
    44.   
    45.     //删除观察者  
    46.   
    47.     @Override  
    48.   
    49.     public void deleteObserver(Observer observer) {  
    50.   
    51.        observers.remove(observer);  
    52.   
    53.     }  
    54.   
    55.    
    56.   
    57.     //通知所有观察者  
    58.   
    59.     @Override  
    60.   
    61.     public void notifyObservers() {  
    62.   
    63.        for(Observer observer:observers)  
    64.   
    65.        {  
    66.   
    67.            observer.update(this, "start");  
    68.   
    69.        }  
    70.   
    71.     }  
    72.   
    73.       
    74.   
    75.     //容器的启动方法,启动容器并调用notifyObservers方法通知所有观察者  
    76.   
    77.     public void start()  
    78.   
    79.     {  
    80.   
    81.        System.out.println("container start");  
    82.   
    83.        notifyObservers();  
    84.   
    85.     }  
    86.   
    87.       
    88.   
    89.     public static void main(String[] args) {  
    90.   
    91.          
    92.   
    93.        ContainerBase container=new ContainerBase();//声明一个容器  
    94.   
    95.        Observer observer=new ContainerConfig();    //声明一个监听类  
    96.   
    97.        container.addObserver(observer);            //为容器添加监听类  
    98.   
    99.        container.start();                          //启动容器  
    100.   
    101.    
    102.   
    103.     }  
    104.   
    105.    
    106.   
    107. }  

      

     

     

     

    具体观察者:

    Java代码  收藏代码
    1. package com.scnulh.observer;  
    2.   
    3.    
    4.   
    5. /** 
    6.  
    7.  * 具体观察者,假设这是一个容器的监听类, 
    8.  
    9.  * 在tomcat容器启动时,处理tomcat的配置 
    10.  
    11.  */  
    12.   
    13. public class ContainerConfig implements Observer{  
    14.   
    15.    
    16.   
    17.     @Override  
    18.   
    19.     public void update(Observable observable, Object arg) {  
    20.   
    21.        String event=(String) arg;  
    22.   
    23.        if(event.equals("start"))  
    24.   
    25.        {  
    26.   
    27.            System.out.println("container starting, do container configs");  
    28.   
    29.        }  
    30.   
    31.     }  
    32.   
    33. }  

      

     

     

             上述便是观察者模式的简单示例,之所以用ContainerBase和ContainerConfig作为具体主题和观察者,是因为后面要分析tomcat的容器(Container)和监听类(Config)的源代码,这里先模拟下他们的工作方式。

             细心的读者很快就会发现,在具体主题ContainerBaser中,对观察者的管理方法其实是很固定的,无非就是声明一个Observer的集合,提供添加,删除,查找的方法。甚至连在主题发生变化时,通知观察者的方法也是固定的,即轮循的通知每一个观察者。如果每一个实现了主题接口的具体主题都要实现这些方法,无疑会造成重复,带来代码编写上的麻烦。为了消除重复,减少麻烦,可以提供一个类,实现主题对观察者的管理及通知。这正是java util包里Observable与Observer所做的。感兴趣的读者可以出看看,这里就不贴代码了。Tomcat没有直接使用这个Observable类,而是另外实现了一个LifecycleSupport类。

             总的来说,观察者模式还是很好理解的,要让观察者模式用于实际,关键有两点,一点要提供主题与观察者的实现,第二是将观察者注册到具体主题中,这样主题发生变化时,才能通知到观察者。

     

     

    三、Tomcat生命周期管理

    理解了观察者模式,Tomcat的生命周期管理便很容易理解了。所涉及的类有:

    • Lifecycle:相当于抽象主题角色,所有的容器类与组件实现类都实现了这个接口。如StandardContext
    • LifecycleListener:相当于抽象观察者角色,具体的实现类有ContextConfig, HostConfig, EngineConfig类,它们在容器启动时与停止时触发。
    • LifecycleEvent:生命周期事件,对主题与发生的事件进行封装。
    • LifecycleSupport:生命周期管理的实用类,提供对观察者的添加,删除及通知观察者的方法。
    • LifecycleException:生命周期异常类。

     

     

    Lifecycle接口

    Java代码  收藏代码
    1. package com.apache.catalina;  
    2.   
    3.    
    4.   
    5. import org.apache.catalina.LifecycleException;  
    6.   
    7.    
    8.   
    9. public interface Lifecycle {      
    10.   
    11.     //生命周期内的六个事件  
    12.   
    13.     public static final String START_EVENT = "start";     
    14.   
    15.     public static final String BEFORE_START_EVENT = "before_start";  
    16.   
    17.     public static final String AFTER_START_EVENT = "after_start";     
    18.   
    19.     public static final String STOP_EVENT = "stop";     
    20.   
    21.     public static final String BEFORE_STOP_EVENT = "before_stop";      
    22.   
    23.     public static final String AFTER_STOP_EVENT = "after_stop";  
    24.   
    25.     //观察者的管理与通知方法  
    26.   
    27.     public void addLifecycleListener(LifecycleListener listener);  
    28.   
    29.     public void removeLifecycleListener(LifecycleListener listener);  
    30.   
    31.     public LifecycleListener[] findLifecycleListeners();  
    32.   
    33.     //主题的启动与停止方法  
    34.   
    35.     public void start()throws LifecycleException;  
    36.   
    37.     public void stop()throws LifecycleException;  
    38.   
    39. }  

     

     

     

    Lifecycle相当于观察者模式中的抽象主题角色(Observable),它定义了添加、删除及通知管理者的方法。

    还定义了与生命周期相关的6个事件。Start和stop方法是Lifecycle最重要的两个方法,分别代表启动与停止。所有四种容器的标准实现类(StandardEngine, StandardHost, StandardContext,StandardWrapper)和基本组件(Logger,Loader,Manager等)的实现类都实现了Lifecycle接口,这意义着它们都是具体的观察者,具有启动和停止方法。容器启动时,主要做三件事:调用组件的启动方法,启动组件;调用子容器的启动方法,启动子容器;通知容器的观察者,使其执行相应的启动动作。子容器启动也做这三件事,这样整个Tomcat便启动了。Tomcat的停止也类似。

     

    LifecycleListener接口:

    Java代码  收藏代码
    1. package org.apache.catalina;  
    2.   
    3. public interface LifecycleListener {  
    4.   
    5.     /** 
    6.  
    7.      * Acknowledge the occurrence of the specified event. 
    8.  
    9.      * 
    10.  
    11.      * @param event LifecycleEvent that has occurred 
    12.  
    13.      */  
    14.   
    15.     public void lifecycleEvent(LifecycleEvent event);  
    16.   
    17.    
    18.   
    19. }  

     

     

     

    LifecycleListener相当于观察者模式中的抽象观察者角色(Observer),可以看到它与Observer非常的类似,都只有一个更新自己的方法。不同的是,Observer 更新方法中,有两个参数:Observable与Object,而LifecycleListener中只有一个参数LifecycleEvent,这正是对前面两个参数的封装。

     

    LifecycleEvent:

    Java代码  收藏代码
    1. package org.apache.catalina;  
    2.   
    3.    
    4.   
    5. import java.util.EventObject;  
    6.   
    7.    
    8.   
    9. public final class LifecycleEvent  
    10.   
    11.     extends EventObject {  
    12.   
    13.       
    14.   
    15.     public LifecycleEvent(Lifecycle lifecycle, String type) {  
    16.   
    17.         this(lifecycle, type, null);  
    18.   
    19.     }  
    20.   
    21.    
    22.   
    23.     public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {  
    24.   
    25.         super(lifecycle);  
    26.   
    27.         this.lifecycle = lifecycle;  
    28.   
    29.         this.type = type;  
    30.   
    31.         this.data = data;  
    32.   
    33.     }  
    34.   
    35.    
    36.   
    37.     private Object data = null;      
    38.   
    39.     private Lifecycle lifecycle = null;      
    40.   
    41.     private String type = null;      
    42.   
    43.       
    44.   
    45.     public Object getData() {  
    46.   
    47.         return (this.data);  
    48.   
    49.     }  
    50.   
    51.     public Lifecycle getLifecycle() {  
    52.   
    53.         return (this.lifecycle);  
    54.   
    55.     }  
    56.   
    57.     public String getType() {  
    58.   
    59.         return (this.type);  
    60.   
    61.     }  
    62.   
    63. }  

     LifecycleEvent

    是对主题(事件源),事件及相关数据的封装,继承自java.util.

     

    是对主题(事件源),事件及相关数据的封装,继承自java.util.

     

    EventObject.

     

    LifecycleSupport:

    前面说过,抽象主题定义的添加,删除和通知观察者的方法都是很固定的,每个实现类实现起来都一样,这样就可以提供一个类来实现这些功能,具体的主题类直接调用便可以了。

    Java代码  收藏代码
    1. package org.apache.catalina.util;  
    2.   
    3.    
    4.   
    5. import org.apache.catalina.Lifecycle;  
    6.   
    7. import org.apache.catalina.LifecycleEvent;  
    8.   
    9. import org.apache.catalina.LifecycleListener;  
    10.   
    11.    
    12.   
    13. public final class LifecycleSupport {  
    14.   
    15.     public LifecycleSupport(Lifecycle lifecycle) {  
    16.   
    17.         super();  
    18.   
    19.         this.lifecycle = lifecycle;  
    20.   
    21.    
    22.   
    23.     }  
    24.   
    25.    
    26.   
    27.      
    28.   
    29.     private Lifecycle lifecycle = null;    
    30.   
    31.     private LifecycleListener listeners[] = new LifecycleListener[0];  
    32.   
    33.     //添加一个观察者  
    34.   
    35.     public void addLifecycleListener(LifecycleListener listener) {  
    36.   
    37.       synchronized (listeners) {  
    38.   
    39.           LifecycleListener results[] =  
    40.   
    41.             new LifecycleListener[listeners.length + 1];  
    42.   
    43.           for (int i = 0; i < listeners.length; i++)  
    44.   
    45.               results[i] = listeners[i];  
    46.   
    47.           results[listeners.length] = listener;  
    48.   
    49.           listeners = results;  
    50.   
    51.       }  
    52.   
    53.    
    54.   
    55.     }  
    56.   
    57.     //找出所注册的观察者  
    58.   
    59.     public LifecycleListener[] findLifecycleListeners() {  
    60.   
    61.         return listeners;  
    62.   
    63.    
    64.   
    65.     }  
    66.   
    67.     //通知观察者  
    68.   
    69.     public void fireLifecycleEvent(String type, Object data) {  
    70.   
    71.    
    72.   
    73.         LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);  
    74.   
    75.         LifecycleListener interested[] = null;  
    76.   
    77.         synchronized (listeners) {  
    78.   
    79.             interested = (LifecycleListener[]) listeners.clone();  
    80.   
    81.         }  
    82.   
    83.         for (int i = 0; i < interested.length; i++)  
    84.   
    85.             interested[i].lifecycleEvent(event);  
    86.   
    87.    
    88.   
    89.     }  
    90.   
    91.     //删除一个观察者  
    92.   
    93.     public void removeLifecycleListener(LifecycleListener listener) {  
    94.   
    95.    
    96.   
    97.         synchronized (listeners) {  
    98.   
    99.             int n = -1;  
    100.   
    101.             for (int i = 0; i < listeners.length; i++) {  
    102.   
    103.                 if (listeners[i] == listener) {  
    104.   
    105.                     n = i;  
    106.   
    107.                     break;  
    108.   
    109.                 }  
    110.   
    111.             }  
    112.   
    113.             if (n < 0)  
    114.   
    115.                 return;  
    116.   
    117.             LifecycleListener results[] =  
    118.   
    119.               new LifecycleListener[listeners.length - 1];  
    120.   
    121.             int j = 0;  
    122.   
    123.             for (int i = 0; i < listeners.length; i++) {  
    124.   
    125.                 if (i != n)  
    126.   
    127.                     results[j++] = listeners[i];  
    128.   
    129.             }  
    130.   
    131.             listeners = results;  
    132.   
    133.         }  
    134.   
    135.    
    136.   
    137.     }  
    138.   
    139. }  

     

     

     

    这样,具体的主题实现抽象主题中对观察者的添加、删除与通知方法便非常简单了。

    如在ContainerBase(容器的基本实现类)中:

     

    Java代码  收藏代码
    1. protected LifecycleSupport lifecycle = new LifecycleSupport(this);  
    2.   
    3.     public void addLifecycleListener(LifecycleListener listener) {  
    4.   
    5.         lifecycle.addLifecycleListener(listener);  
    6.   
    7.     }  
    8.   
    9.    
    10.   
    11.     public LifecycleListener[] findLifecycleListeners() {  
    12.   
    13.         return lifecycle.findLifecycleListeners();  
    14.   
    15. }  
    16.   
    17.    
    18.   
    19.     public void removeLifecycleListener(LifecycleListener listener) {  
    20.   
    21.         lifecycle.removeLifecycleListener(listener);  
    22.   
    23. }  

     

     

    Lifecycle,LifecycleListener,LifecycleSupport,LifecycleEvent, LifecycleException及其具体的观察者与具体的主题之间的关系如下:

     结构图

     

     

     

     

     

     

     

    让我们再来看一下StandardContext的启动方法,StandartContext实现了Lifecycle,它的启动方法由上一级容器所调用。

    public synchronized void start() throws LifecycleException {

    //略过N多代码

     

    //通知观察者,容器即将启动

    lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

     

    //略过N多代码

    // 启动loader,cluster,realm组件

    if ((loader != null) && (loader instanceof Lifecycle))

             ((Lifecycle) loader).start();

    if ((cluster != null) && (cluster instanceof Lifecycle))

             ((Lifecycle) cluster).start();

    if ((realm != null) && (realm instanceof Lifecycle))

             ((Lifecycle) realm).start();

    //略过N多代码

     

    //找出所有的子容器,并且启动

    Container children[] = findChildren();

    for (int i = 0; i < children.length; i++) {

          if (children[i] instanceof Lifecycle)

                ((Lifecycle) children[i]).start();

      }

     

    //通知所有观察者,容器正在启动

    lifecycle.fireLifecycleEvent(START_EVENT, null);

     

    //启动Manager组件

    if ((manager != null) && (manager instanceof Lifecycle))

             ((Lifecycle) manager).start();

    //通知所有观察者,容器已经启动

    lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

     

    }

    这里主要做三件事:调用组件的启动方法,启动组件;调用子容器的启动方法,启动子容器;通知容器的观察者,使其执行相应的启动动作。每一层次的容器都这样启动,最终整个Tomcat启动完毕。

    因为源代码实在是太多了,没法全部贴出来,如果感兴趣,大家可以在附件上下下来研究。

    推荐大家阅读How Tomcat Works这本书。就像书名一样,这本书详细的剖析了Tomcat运作机制,写的非常的好,在豆瓣这本书的评分是9.4分,而同样经典的Thinking in Java9.1分,Effective Java9.2分。Tomcat的源代码非常值得研究,里面用了很多的设计模式,如本文讲的观察者模式,还是上一篇所讲的单例模式,以及门面模式,责任链模式等等。

     

  • 相关阅读:
    详细介绍Linux shell脚本基础学习(二)
    MySQL主从复制
    推荐一款好用的jquery弹出层插件——wbox
    Jenkins安装插件下载失败
    如何在 Amazon RDS 中部署 MySQL 数据库实例
    VMware vSphere 6 Enterprise Plus 永久激活许可证亲测可用
    使用 convert database 命令进行 RMAN 跨平台迁移(12C>19C)
    hbase用户授权
    hbase move region
    hbase表集群间数据同步 hbase replication
  • 原文地址:https://www.cnblogs.com/chenying99/p/2671199.html
Copyright © 2020-2023  润新知