设为首页 收藏本站
查看: 852|回复: 0

[经验分享] Tomcat生命周期管理与观察者模式

[复制链接]

尚未签到

发表于 2015-8-8 11:11:10 | 显示全部楼层 |阅读模式
  本文主要结合观察者模式,讲述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;
  观察者模式的结构:
   DSC0000.jpg
  
  观察者模式包括以下角色:
  抽象主题(Observable):定义了管理观察者的添加,删除和通知方法。
  抽象观察者(Observer):定义了主题发生变化时,具体观察者必须执行的方法。
  具体主题(Container):对观察者进行管理,并在自身发生变化时,通知观察者。
  具体观察者(ContainerConfig):实现了当主题发生变化时,应该执行的动作。
  
  下面是观察者模式的示例代码:
  package com.scnulh.observer;
  抽象主题:
  
  



Java代码 DSC0001.png

  • /**

  • * 抽象主题

  • */

  • public interface Observable {



  •     //添加观察者

  •     void addObserver(Observer observer);

  •     //删除观察者

  •     void deleteObserver(Observer observer);

  •     //在事件发生时,通知观察者

  •     void notifyObservers();

  • }
  
  
  
  抽象观察者:



Java代码

  • package com.scnulh.observer;



  • /**

  • * 抽象观察者

  • */

  • public interface Observer {



  •     /**

  •      * 更新方法,观察者根据传入的主题对象获取主题的上下文

  •      * 根据传入的Object对象判断发生了何种事件

  •      * @param observable

  •      * @param arg

  •      */

  •     void update(Observable observable,Object arg);



  • }
  
  
  
  
  具体主题:
  



Java代码

  • package com.scnulh.observer;



  • import java.util.ArrayList;

  • import java.util.List;



  • /**

  • * 具体主题,假设这是一个Tomcat的容器基类

  • * 有一个start方法,代表容器的启动,在启动过程中

  • * 通知所有观察者

  • */

  • public class ContainerBase implements Observable{



  •     //持有观察者的List

  •     private List observers=new ArrayList();





  •     //添加观察者

  •     @Override

  •     public void addObserver(Observer observer) {

  •        observers.add(observer);

  •     }



  •     //删除观察者

  •     @Override

  •     public void deleteObserver(Observer observer) {

  •        observers.remove(observer);

  •     }



  •     //通知所有观察者

  •     @Override

  •     public void notifyObservers() {

  •        for(Observer observer:observers)

  •        {

  •            observer.update(this, "start");

  •        }

  •     }



  •     //容器的启动方法,启动容器并调用notifyObservers方法通知所有观察者

  •     public void start()

  •     {

  •        System.out.println("container start");

  •        notifyObservers();

  •     }



  •     public static void main(String[] args) {



  •        ContainerBase container=new ContainerBase();//声明一个容器

  •        Observer observer=new ContainerConfig();    //声明一个监听类

  •        container.addObserver(observer);            //为容器添加监听类

  •        container.start();                          //启动容器



  •     }



  • }
  
  
  
  
  
  具体观察者:
  



Java代码

  • package com.scnulh.observer;



  • /**

  • * 具体观察者,假设这是一个容器的监听类,

  • * 在tomcat容器启动时,处理tomcat的配置

  • */

  • public class ContainerConfig implements Observer{



  •     @Override

  •     public void update(Observable observable, Object arg) {

  •        String event=(String) arg;

  •        if(event.equals("start"))

  •        {

  •            System.out.println("container starting, do container configs");

  •        }

  •     }

  • }
  
  
  
  
           上述便是观察者模式的简单示例,之所以用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代码

  • package com.apache.catalina;



  • import org.apache.catalina.LifecycleException;



  • public interface Lifecycle {

  •     //生命周期内的六个事件

  •     public static final String START_EVENT = "start";

  •     public static final String BEFORE_START_EVENT = "before_start";

  •     public static final String AFTER_START_EVENT = "after_start";

  •     public static final String STOP_EVENT = "stop";

  •     public static final String BEFORE_STOP_EVENT = "before_stop";

  •     public static final String AFTER_STOP_EVENT = "after_stop";

  •     //观察者的管理与通知方法

  •     public void addLifecycleListener(LifecycleListener listener);

  •     public void removeLifecycleListener(LifecycleListener listener);

  •     public LifecycleListener[] findLifecycleListeners();

  •     //主题的启动与停止方法

  •     public void start()throws LifecycleException;

  •     public void stop()throws LifecycleException;

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



Java代码

  • package org.apache.catalina;

  • public interface LifecycleListener {

  •     /**

  •      * Acknowledge the occurrence of the specified event.

  •      *

  •      * @param event LifecycleEvent that has occurred

  •      */

  •     public void lifecycleEvent(LifecycleEvent event);



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



Java代码

  • package org.apache.catalina;



  • import java.util.EventObject;



  • public final class LifecycleEvent

  •     extends EventObject {



  •     public LifecycleEvent(Lifecycle lifecycle, String type) {

  •         this(lifecycle, type, null);

  •     }



  •     public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {

  •         super(lifecycle);

  •         this.lifecycle = lifecycle;

  •         this.type = type;

  •         this.data = data;

  •     }



  •     private Object data = null;

  •     private Lifecycle lifecycle = null;

  •     private String type = null;



  •     public Object getData() {

  •         return (this.data);

  •     }

  •     public Lifecycle getLifecycle() {

  •         return (this.lifecycle);

  •     }

  •     public String getType() {

  •         return (this.type);

  •     }

  • }
  LifecycleEvent
是对主题(事件源),事件及相关数据的封装,继承自java.util.  
是对主题(事件源),事件及相关数据的封装,继承自java.util.  
  
EventObject.  
  LifecycleSupport:
  前面说过,抽象主题定义的添加,删除和通知观察者的方法都是很固定的,每个实现类实现起来都一样,这样就可以提供一个类来实现这些功能,具体的主题类直接调用便可以了。
  



Java代码

  • package org.apache.catalina.util;



  • import org.apache.catalina.Lifecycle;

  • import org.apache.catalina.LifecycleEvent;

  • import org.apache.catalina.LifecycleListener;



  • public final class LifecycleSupport {

  •     public LifecycleSupport(Lifecycle lifecycle) {

  •         super();

  •         this.lifecycle = lifecycle;



  •     }





  •     private Lifecycle lifecycle = null;

  •     private LifecycleListener listeners[] = new LifecycleListener[0];

  •     //添加一个观察者

  •     public void addLifecycleListener(LifecycleListener listener) {

  •       synchronized (listeners) {

  •           LifecycleListener results[] =

  •             new LifecycleListener[listeners.length + 1];  

  •           for (int i = 0; i < listeners.length; i++)

  •               results = listeners;

  •           results[listeners.length] = listener;  

  •           listeners = results;

  •       }



  •     }

  •     //找出所注册的观察者

  •     public LifecycleListener[] findLifecycleListeners() {

  •         return listeners;



  •     }

  •     //通知观察者

  •     public void fireLifecycleEvent(String type, Object data) {



  •         LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);

  •         LifecycleListener interested[] = null;

  •         synchronized (listeners) {

  •             interested = (LifecycleListener[]) listeners.clone();

  •         }

  •         for (int i = 0; i < interested.length; i++)

  •             interested.lifecycleEvent(event);



  •     }

  •     //删除一个观察者

  •     public void removeLifecycleListener(LifecycleListener listener) {



  •         synchronized (listeners) {

  •             int n = -1;

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

  •                 if (listeners == listener) {

  •                     n = i;

  •                     break;

  •                 }

  •             }

  •             if (n < 0)

  •                 return;

  •             LifecycleListener results[] =

  •               new LifecycleListener[listeners.length - 1];  

  •             int j = 0;

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

  •                 if (i != n)

  •                     results[j++] = listeners;

  •             }

  •             listeners = results;

  •         }



  •     }

  • }
  
  
  
  
  
  这样,具体的主题实现抽象主题中对观察者的添加、删除与通知方法便非常简单了。
  如在ContainerBase(容器的基本实现类)中:
  



Java代码

  • protected LifecycleSupport lifecycle = new LifecycleSupport(this);

  •     public void addLifecycleListener(LifecycleListener listener) {

  •         lifecycle.addLifecycleListener(listener);

  •     }



  •     public LifecycleListener[] findLifecycleListeners() {

  •         return lifecycle.findLifecycleListeners();

  • }



  •     public void removeLifecycleListener(LifecycleListener listener) {

  •         lifecycle.removeLifecycleListener(listener);

  • }
  
  
  Lifecycle,LifecycleListener,LifecycleSupport,LifecycleEvent, LifecycleException及其具体的观察者与具体的主题之间的关系如下:
   DSC0002.jpg
  
  
  
  
  
  
  
  让我们再来看一下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 instanceof Lifecycle)
              ((Lifecycle) children).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 Java为9.1分,Effective Java为9.2分。Tomcat的源代码非常值得研究,里面用了很多的设计模式,如本文讲的观察者模式,还是上一篇所讲的单例模式,以及门面模式,责任链模式等等。
  


  • HowTomcatWorksApps.zip (3.7 MB)
  • 下载次数: 11

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-95541-1-1.html 上篇帖子: Tomcat的server.xml配置节点简介 下篇帖子: Tomcat 性能优化(连接数、线程、JVM、dir)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表