原来tomcat是这么玩的(三)组件启动流程

4 篇文章 0 订阅

前面一篇文章中记载了tomcat中几大组件是如何初始化的,这里记载一下组件如何启动。通过前面我们知道组件的初始化其实调用的生命周期中的init方法,那么我们可以猜测启动是不是也是调用的生命周期中的start方法。

调用engine和connector的start()入口

Bootstrap调用start 方法 进入Catalina然后调用StandardServer的start,再通过StandardServer启动StandardServer中的service来启动engine和connector

容器部分的启动

engine的启动过程

engine的启动过程其实是调用的父类ContainerBase的startInternal方法。
在这里插入图片描述
接下来我们到源码中验证一下上图的流程
StandardService
当我们跟到StandardEngine的startInternal方法会发现其实里面只是调用了父类的startInternal方法
StandardEngine
跟入父类的startInternal
ContainerBase
这个里面有2个比较关键的方法;

  1. 根据下面这段代码 我们很清楚的可以看出其实这个for循环就是在调用子容器的start方法
// 这里只摘取代码片段
    results.add(startStopExecutor.submit(new StartChild(children[i])));
	private static class StartChild implements Callable<Void> {
       private Container child;
       public StartChild(Container child) {this.child = child;}
             @Override
       public Void call() throws LifecycleException {
           child.start();
           return null;
       }
   }
  1. 谈到第二个关键的地方之前我们先回忆一下,在init过程中,我们的容器部分只初始化到了engine,而host 、context、wrapper都没有进行初始化调用,其实这3个组件的初始化发生在这个阶段。
    首先我们关注一下LifecycleBase 这个类,这个类是Lifecycle接口的最底层实现,我们可以看到一行关键的代码
    // 默认组件的生命周期是NEW
      private volatile LifecycleState state = LifecycleState.NEW;
      
    
    然后我们在去看此类中的start方法默认实现,我们会发现上面问题的答案就包含在这里
    LifecycleBase
    这样在启动容器的时候如果当前生命周期为new的时候会调用初始化方法,初始化组件。
    回到我们刚才ContainerBase 的 startInternal方法
    ContainerBase
    这个地方使用到了事件驱动编程,当tomcat中组件每切换一个状态都会通知它的生命周期监听器去做一些响应。
host启动

与engine中不同的是在 StandardHost的startInternal方法中多了一个对ErrorReportValve的处理,我们后面在讨论value的作用。
然后我们会发现后面又到了在service中启动engine中降到的类ContainerBase的startInternal方法,由于上面我们分析过了engnie的启动过程,那么这里我们很容易得出结论,此处是在启动host的时候启动了context。所以我们需要关注Host在启动的时候 它的监听器干了什么。
HostConfig
在这里我们会发现一个很敏感的词汇 deploy,然后 我们再跟进去,会发现这里实现了tomcat 3种部署方式
HostConfig
具体的部署代码太多了,这里只拿我们关心的几行来看一看

  1. 此处就是为什么startStopThreads属性可以控制同时有多少个应用并发部署。HostConfig
  2. context的解析时机
    HostConfig
    这行代码是不是分外熟悉,在上一节tomcat的组件初始化中提到过,tomcat通过Digester实现了对xml配置文件的解析。然后我们在跟下去。
    HostConfig
    这里就实现了向host中加入context 完成应用的部署我们由上面的uml图知道容器会先findChildren,然后再启动这些子容器,最后再发出通知,当看到这个地方的时候是不是会有一个疑惑,我的子容器都已经start完毕了,这个时候才去通知监听器做应用部署,那不是没有时机启动?我们接着跟下去 会发现一切豁然开朗。
    ContainerBase
    ContainerBase
context启动

context启动过程中于前面的engine和host有点不同,StandardContext中的startInternal有自己的实现,虽然这个方法巴拉巴拉一大堆,但是我们此次关心的只有其中一部分。

  1. 构建一个webApp 级别的类加载器,这里又验证了在Bootstrap在load时候打破双亲委派机制的类加载器关系
    ContextConfig
  2. 注入wrapper容器并启动
    ContextConfig
    这个里面关键的代码在ContextConfig的webConfig方法,这个方法里面同样是一大堆逻辑,它大概的意思就是将配置信息组成了一个WebXml对象,同样进入我们所关心的核心方法configureContext
    ContextConfig
    在这个方法内我们又可以看到很多熟悉的东西,
    ContextConfig
    继续往下,我们终于发现了关键的代码
    ContextConfig
    在这段代码中出现了3个关键信息,createWrapper,setServletClass,addChild。至此我们的context中就加入了wrapper组件。
wrapper 启动

看了这么多关于context中如何创建注入wrapper的图,我们再来回顾下context创建wrapper的入口
ContextConfig
紧接着fireLifecycleEvent的执行完毕,我们的wrapper也随之启动。
wrapper的启动相对context而言就要简单的多了
StandardWrapper

连接器部分的启动

相对于容器部分的启动而言,连接器部分就要简单很多,因为连接器需要干的事情在init阶段已经干的差不多了。

Connector的启动

连接器中主要干的一件事是启动ProtocolHandler
Connector

ProtocolHandler的启动

ProtocolHandler 启动时干的事情主要是通过父类AbstractProtocol
启动EndpointProtocolHandler

Endpoint的启动

虽然endpoint启动的时候可以去调用bind(),但是如果设置了bindOnInit = true,在init阶段它就会去执行bind()
AbstractEndPoint
至于startInternal就要根据io的不同执行不同的子类,这里我们以NioEndpoint为例子
NioEndPoint
这个方法里面主要就是构造和启动acceptor、poller线程

NioEndPoint
tomcat组件启动时序图,没有详细画host构建context,context构建wrapper的过程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值