Tomcat源码分析-Lifecycle

         最近在对tomcat前段时间的学习做一个总结,前前后后还是花了不少时间。
         下面是我对Lifecycle在tomcat中的理解。
             看过Tomcat源码或者相关书籍的童鞋应该都知道,Tomcat中几乎所有组件,资源都直接或者间接的继承了LifecycleBase这个抽象类,见名知意该类是组件生命周期的象征,就如同自然界的生命周期一样,人有幼年期,青春期,青年期,壮年期,暮年期,组件对应的也有这些类似时期,在Tomcat对应称之为状态,在不同时期也具有不同的责任;与人不同的是,人无法返老还童,而这里的组件在某些情况下却可以。
        Tomcat正是通过这种思想使代码结构分明,职责清晰,代码通读易懂。
        要知道Lifecycle在tomcat中具体起了作用,想必解答了如下疑问想必就清楚了。
        1、Lifecycle为tomcat内部元件提供了什么样的服务?位置如何?
        2、Lifecycle相关的结构设计?状态是如何扭转的?
        3、Lifecycle在初始化和启动时基本逻辑是怎样滴?
        4、各组件在初始状态和启动状态时分别做了什么?监听器起了什么作用?
        5、各组件启动后tomcat是怎么保持提供服务的,而非是生命周期结束?
        
          首先来回答第一个问题。
         1、Lifecycle为tomcat内部元件提供了什么样的服务?位置如何?
         Lifecycle为tomcat元件提供了核心的基础服务,定义了不同状态为各元件提供了不同服务并且状态变化时提供“通知”机制,下图是Lifecycle在整个Tomcat中的位置。
          

         在来回答第二问题
         2、Lifecycle相关的结构设计?是如何把事件组织在一起的?状态是如何扭转的?
          如下是Lifecycle中的类图
          
                                                                    (特别说明:实际图片较大,双击放大看可能更清楚些)
         比较典型的运用了其中两种设计模式,其一是观察者模式,此处目标对象是Lifecycle,具体目标对象为StandardServer,StandardContext,Connector等,这些具体目标对象中都提供了一个数组来维护观察者,当目标对象的状态发生改变时,则会把状态信息与目标对象封装成一个LifecycleEvent通知对应的观察者。而另一模式则为模板方法模式,在实现接口Lifecycle时,定义了抽象类LifecycleBase,该抽象类具体定义了接口方法的实现步骤,并通过xxxInternal抽象方法让子类真正完成整个xxx过程。
         状态扭转示意图
        
          LifecycleBase中各init,start等方法的逻辑其实就很好的反应了上述状态扭转的变化。
         
          第三个问题:
       3、Lifecycle在初始化和启动时基本逻辑是怎样滴?
        初始化即调用init方法,首先判断状态是不是新增态,有且仅有是新增状态(NEW)的lifecycle实例才可以进行初始化,否则抛异常提示;继续初始化置lifecycle实例为正在初始化(INITIALIZING)状态并触发相应的监听器做处理,之后在回调具体lifecycle实例的initInternal方法做进一步初始化,此过程中还会初始化相关联的组件,如这过程执行异常,则会置初始化失败并触发监听器做处理,否则置初始化状态为已初始化完成(INITIALIZED)并执行相关监听器动作,第一个初始化的组件为StandardServer,该组件初始化完成也意味着与之相关的组件也初始化完成。
        StandardServer组件初始化完后则进行启动(start)操作,tomcat中并不是所有的组件都是在StandardServer组件初始化(init)过程中完成初始化的,还有一部分是在StandardServer启动时进行初始化的,初始化与启动的边界可通过后面的时序图看出。
        启动时会首先判断组件的状态,如果状态是准备启动,启动中,或者是已启动则直接结束,不进行后续的动作,否则在根据状态继续判断,如是新增态,则进行初始化操作,如StandardWrapper在启动的过程中并不是已初始化状态而是新增状态,所以会先进行初始化,初始化完毕后在进行后续的启动步骤,如组件状态是失败,则停止启动。只有初始化完毕的组件才会进入真正的启动,首先是置组件状态为准备启动并通知各监听器执行相关动作,之后回调该组件的startInternal方法完成自己以及相关组件的启动操作,并在startInternal方法快要结束时设置组件状态为正在启动-setState(LifecycleState.STARTING)并触发监听器执行相关操作 ,与其它几次置状态不同的是,正在启动过程中的这个状态是具体子类来置的,而非抽象类LifecycleBase置的,LifecycleBase只对组件状态合理性判断,判断通过说明具体子类按照父类的约定完成了启动,而只要在startInternal抛出任何异常,则置为失败状态并通知监听器处理,如正常,则在判断此时组件状态是不是正在启动中(STARTING),如是说明startInternal整个过程中是ok的,最后在置状态为完成启动(STARTED)并让监听器执行相关动作,至此整个初始化和启动过程完毕。
         而组件之间关系的维护,目标对象与监听器之间关系的维护一部分是在Catalina加载(load)的过程中建立的,另部分是在组件启动过程中建立的。
          
         在来回答第四个问题
        4、各组件在初始状态和启动状态时分别做了什么?监听器是如何起作用的?
        下面是Catalina启动时的时序图
       
                                                                (特别说明:此图放大看效果更清晰些)
 
       重要节点说明:
       (1)StandardService在初始化StandardEngine,Executor,Connector相关组件后,StandardServer初始化结束,之后进行启动操作,四个容器对象中只有StandardEngine的初始化是在StandardServer初始化过程中完成的,另外几个容器对象StandardHost,StandardContext,StandardWrapper是在StandardServer启动的过程中完成初始化和启动的,StandardService是初始化结束与启动的边界。
       (2)StandardService起着一个对外服务的角色与位置,对外通过StandardService关联的连接器Connector对外接受请求,对内在把请求数据转给关联的引擎进行真正的处理,该组件为Connector与引擎提供了一个上下文环境。
       (3)StandardHost代表虚拟主机的意思,该组件状态被设置成正在启动时触发其监听器HostConfig来初始化与启动对应应用(StandardContext),此处根据应用类型的形式,如是war的形式,还是目录的形式,使用了不同的任务来处理。此处通过获取StandardHost中的线程池来启动各应用,同种类型的应用启动是并行启动的,只有当这种类型的应用都启动完后,才会启动下一批不同类型的应用,因此不同类型应用的启动是串行的。
       (4)StandardContext代表的是应用(app)的含义,对于一个应用而言,它是有结构,有约束定义的。应用与其部署的环境,承载的容器往往又是紧密相关的,这是生产厂商实际决定的,他可以有自己的一套逻辑,或者遵循这方面的规范去实现。而tomcat中的应用(app)也是有一些规定的,如应用(app)的目录结构是有一定要求的,其中的context.xml与web.xml也是有一定格式的,因此StandardContext实则是按这些规定来加载应用对应的jar,维护context.xml,web.xml各标签对应类之间的关系的。
       (5)StandardContext在启动的过程中还是做了不少事滴,首先启动该应用的资源(namingResources),绑定资源目录,其次创建应用加载器并与当前容器绑定,之后启动应用加载器,这个过程实则是实例化了一个应用类加载器,而该应用类加载器是依附于应用加载器的,在设置类加载器路径,是否委托等。启动应用加载器后,会将该应用类加载器设置成线程上下文类加载器,之后便触发监听器的配置事件(CONFIGURE_START_EVENT),让ContextConfig监听器来处理该应用存在的context.xml与web.xml配置文件的解析注,注射等一系列工作。这里面会把web.xml解析后的信息首先封装到webxml这对象中,之后在把webxml这个对象的信息传给对应的StandardContext进一步完成与其他组件关系的建立;也是在webxml这个类的逻辑中,创建了StandardWrapper,之后又进行了初始化和启动,之后根据webxml中的内容对StandardWrapper做了一些赋值。配置事件结束后,相应的参数,监听器,过滤器都实例化,并已在StandardContext中维护好了。之后StandardContext启动与之相关联的管道,创建session并与之绑定,启动session,合并参数,之后在启动这些监听器,过滤器,还有一些需要随应用启动要启动的一些实例,最后在更新组件状态触发事件。与其他几个容器对象不同的是,StandardContext中的startInternal方法没有调用父类的startInternal方法。   
       (6)StandardWrapper其实代表了一个Servlet,它在启动时需要将其联系的管道启动,最后更新状态并通知监听器执行相应的步骤即可。
       (7)Connector是连接前端请求数据与容器的桥梁,connector在启动时,则会启动相应的协议处理者,http是在tcp协议之上的应用层协议,因此会进一步启动tcp对应的抽象对象,最终会启动一个后台线程监听8000端口处理http请求。在tomcat中默认有两个连接器,另一个是ajp协议,该协议可用于tomcat与apache之间的通讯,该协议最终也会开启一个后台线程监听相应端口。最后在启动Connector与之对应的监听器MapperListener。
      (8)MapperLister监听器在启动时说明前面几乎大多数组件已初始化和启动完毕,之后找到默认的虚拟主机,将虚拟主机名称设置到该监听器维护的mapper中,而该mapper的数据结构映射了servlet请求被划分的逻辑单元;mapper设置虚拟主机后,会将这个监听器注册到这个连接器关联的Engine中以及engine关联的容器中,之后又把请求资源,wrapper,上下文路径等信息封装到mapper这个数据结构中,封装完毕也意味着mapper中6个内部类的实例化,关系的建立,以及信息填充完毕。
       (9)webxml是Digester根据规则WebRuleSet解析web.xml信息封装的一个载体,里面有许多与StandardContext,StandardWrapper的交互的方法,在此就不在赘述了。
       (10)StandardPipeline可以理解成一个管道,各容器启动后,管道也实现了相连,当接受到请求时,请求最终会通过容器的管道传输请求找到真正处理的Wrapper进行处理。

      最后在来回答the last question
      5、各组件启动后tomcat是怎么保持提供服务的,而非是生命周期结束?
      Tomcat在加载前就设置了Catalina await属性为true,最终启动各组件后,会根据该属性值来做判断,如果为是则会一直监听server标签中属性值stutdown端口,因此会一直处于阻塞状态,当jvm中只要有一个非后台线程没有结束,jvm就不会关闭,tomcat就能一直提供服务。
      具体是如何接受数据处理请求的,在此就不赘述,后续会另写另一篇文章分析。

     

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值