TOMCAT源码分析(启动框架)

1/5/2008来源:Java教程人气:4447


  前言:
  本文是我阅读了TOMCAT源码后的一些心得。 主要是讲解TOMCAT的系统框架, 以及启动流程。若有错漏之处,敬请批评指教!
  
  建议:
  究竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么轻易把握TOMCAT的框架的。 所以得实践、实践、再实践。 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动过程。 假如有不明白的地方, 再来查阅本文, 看是否能得到帮助。 我相信这样效果以及学习速度都会好很多!
  
  1. Tomcat的整体框架结构
  Tomcat的基本框架, 分为4个层次。
  Top Level Elements:
  Server
  Service
  Connector
  HTTP
  AJP
  Container
  Engine
  Host
  Context
  Component
  manager
  logger
  loader
  pipeline
  valve
  ...
  站在框架的顶层的是Server和Service
  Server: 其实就是BackGroud程序, 在Tomcat里面的Server的用处是启动和监听服务端事件(诸如重启、关闭等命令。 在tomcat的标准配置文件:server.xml里面, 我们可以看到“”这里的"SHUTDOWN"就是server在监听服务端事件的时候所使用的命令字)
  Service: 在tomcat里面, service是指一类问题的解决方案。 通常我们会默认使用tomcat提供的:Tomcat-Standalone 模式的service。 在这种方式下的service既给我们提供解析jsp和servlet的服务, 同时也提供给我们解析静态文本的服务。
  
  Connector: Tomcat都是在容器里面处理问题的, 而容器又到哪里去取得输入信息呢?
  Connector就是专干这个的。 他会把从socket传递过来的数据, 封装成Request, 传递给容器来处理。
  通常我们会用到两种Connector,一种叫http connectoer, 用来传递http需求的。 另一种叫AJP, 在我们整合apache与tomcat工作的时候, apache与tomcat之间就是通过这个协议来互动的。 (说到apache与tomcat的整合工作, 通常我们的目的是为了让apache 获取静态资源, 而让tomcat来解析动态的jsp或者servlet。)
  Container: 当http connector把需求传递给顶级的container: Engin的时候, 我们的视线就应该移动到Container这个层面来了。
  在Container这个层, 我们包含了3种容器: Engin, Host, Context.
  Engin: 收到service传递过来的需求, 处理后, 将结果返回给service( service 是通过 connector 这个媒介来和Engin互动的 ).
  Host: Engin收到service传递过来的需求后,不会自己处理, 而是交给合适的Host来处理。
  Host在这里就是虚拟主机的意思, 通常我们都只会使用一个主机,既“localhost”本地机来处理。
  Context: Host接到了从Host传过来的需求后, 也不会自己处理, 而是交给合适的Context来处理。
  比如:
  
  前者交给foo这个Context来处理, 后者交给bar这个Context来处理。
  很明显吧! context的意思其实就是一个web app的意思。
  我们通常都会在server.xml里面做这样的配置
  
  这个context容器,就是用来干我们该干的事儿的地方的。
  
  Compenent: 接下来, 我们继续讲讲component是干什么用的。
  我们得先理解一下容器和组件的关系。
  需求被传递到了容器里面, 在合适的时候, 会传递给下一个容器处理。
  而容器里面又盛装着各种各样的组件, 我们可以理解为提供各种各样的增值服务。
  manager: 当一个容器里面装了manager组件后,这个容器就支持session治理了, 事实上在tomcat里面的session治理, 就是靠的在context里面装的manager component.
  logger: 当一个容器里面装了logger组件后, 这个容器里所发生的事情, 就被该组件记录下来啦! 我们通常会在logs/ 这个目录下看见 catalina_log.time.txt 以及 localhost.time.txt 和localhost_examples_log.time.txt。 这就是因为我们分别为:engin, host以及context(examples)这三个容器安装了logger组件, 这也是默认安装, 又叫做标配 :)
  loader: loader这个组件通常只会给我们的context容器使用, loader是用来启动context以及治理这个context的classloader用的。
  pipline: pipeline是这样一个东西, 当一个容器决定了要把从上级传递过来的需求交给子容器的时候, 他就把这个需求放进容器的管道(pipeline)里面去。 而需求傻呼呼得在管道里面流动的时候, 就会被管道里面的各个阀门拦截下来。 比如管道里面放了两个阀门。 第一个阀门叫做“access_allow_vavle”, 也就是说需求流过来的时候,它会看这个需求是哪个IP过来的, 假如这个IP已经在黑名单里面了, sure, 杀! 第二个阀门叫做“defaul_access_valve”它会做例行的检查, 假如通过的话,OK, 把需求传递给当前容器的子容器。 就是通过这种方式, 需求就在各个容器里面传递,流动, 最后抵达目的地的了。
  valve: 就是上面所说的阀门啦。
  Tomcat里面大概就是这么些东西, 我们可以简单地这么理解tomcat的框架,它是一种自上而下, 容器里又包含子容器的这样一种结构。
  2. Tomcat的启动流程
  这篇文章是讲tomcat怎么启动的,既然我们大体上了解了TOMCAT的框架结构了, 那么我们可以望文生意地就猜到tomcat的启动, 会先启动父容器,然后逐个启动里面的子容器。 启动每一个容器的时候, 都会启动安插在他身上的组件。 当所有的组件启动完毕, 所有的容器启动完毕的时候, tomcat本身也就启动完毕了。
  顺理成章地, 我们同样可以猜到, tomcat的启动会分成两大部分, 第一步是装配工作。 第二步是启动工作。
  装配工作就是为父容器装上子容器, 为各个容器安插进组件的工作。 这个地方我们会用到digester模式, 至于digester模式什么, 有什么用, 怎么工作的. 请参考
  启动工作是在装配工作之后, 一旦装配成功了, 我们就只需要点燃最上面的一根导线, 整个tomcat就会被激活起来。 这就好比我们要开一辆已经装配好了的汽车的时候一样,我们只要把钥匙插进钥匙孔,一拧,汽车的引擎就会发动起来,空调就会开起来, 安全装置就会生效, 如此一来,汽车整个就发动起来了。(这个过程确实和TOMCAT的启动过程不谋而和, 让我们不得不怀疑 TOMCAT的设计者是在GE做java开发的)。
  2.1 一些有意思的名称:
  Catalina
  Tomcat
  Bootstrap
  Engin
  Host
  Context
  他们的意思很有意思:
  Catalina: 远程轰炸机
  Tomcat: 熊猫轰炸机 -- 轰炸机的一种(这让我想起了让国人引以为豪的熊猫手机,是不是英文可以叫做tomcat??? , 又让我想起了另一则广告: 波导-手机中的战斗机、波音-客机中的战斗机 )
  Bootstap: 引导
  Engin: 发动机
  Host: 主机,领土
  Context: 内容, 目标, 上下文
  
  ... 在许多许多年后, 现代人类已经灭绝。 后现代生物发现了这些单词零落零落在一块。 一个自以为聪明的家伙把这些东西翻译出来了:
  在地勤人员的引导(bootstrap)下, 一架轰炸架(catalina)腾空跃起, 远看是熊猫轰炸机(tomcat), 近看还是熊猫轰炸机! 凭借着优秀的发动机技术(engin), 这架熊猫轰炸机飞临了敌国的领土上空(host), 对准目标(context)投下了毁天灭地的核弹头,波~ 现代生物就这么隔屁了~
  
  综上所述, 这又不得不让人联想到GE是不是也参与了军事设备的生产呢?
  反对美帝国主义! 反对美霸权主义! 和平万岁! 自由万岁!
  
  2.2 历史就是那么惊人的相似! tomcat的启动就是从org.apache.catalina.startup.Bootstrap这个类悍然启动的!
  在Bootstrap里做了两件事:
  1. 指定了3种类型classloader:
  commonLoader: common/classes、common/lib、common/endorsed
  catalinaLoader: server/classes、server/lib、commonLoader
  sharedLoader: shared/classes、shared/lib、commonLoader
  2. 引导Catalina的启动。
  用Reflection技术调用org.apache.catalina.startup.Catalina的PRocess方法, 并传递参数过去。
  
  2.3 Catalina.java
  Catalina完成了几个重要的任务:
  1. 使用Digester技术装配tomcat各个容器与组件。
  1.1 装配工作的主要内容是安装各个大件。 比如server下有什么样的servcie。 Host会容纳多少个context。 Context都会使用到哪些组件等等。
  1.2 同时呢, 在装配工作这一步, 还完成了mbeans的配置工作。 在这里,我简单地但不十分精确地描述一下mbean是什么,干什么用的。
  我们自己生成的对象, 自己治理, 天经地义! 但是假如我们创建了对象了, 想让别人来管, 怎么办呢? 我想至少得告诉别人我们都有什么, 以及通过什么方法可以找到 吧! JMX技术给我们提供了一种手段。 JMX里面主要有3种东西。Mbean, agent, connector.
  Mbean: 用来映射我们的对象。也许mbean就是我们创建的对象, 也许不是, 但有了它, 就可以引用到我们的对象了。
  Agent: 通过它, 就可以找到mbean了。
  Connector: 连接Agent的方式。 可以是http的, 也可以是rmi的,还可以直接通过socket。
  发生在tomcat 装配过程中的事情: GlobalResourcesLifecycleListener 类的初始化会被触发:
  protected static Registry registry = MBeanUtils.createRegistry(); 会运行