Tomcat7源码解析

Tomcat7 源码阅读学习

    背景
    Tomcat源码目录结构
    Tomcat体系结构
    Tomcat源码解析
          Tomcat的启动流程
          Tomcat一次完整请求的处理流程
          Tomcat的关闭流程
          Tomcat的Connector组件
          Tomcat的运行过程中的线程概况及线程模型
          Tomcat的类加载机制
          Tomcat所涉及的设计模式
   参考资源

一、背景

        Tomcat作为JavaWeb领域的Web容器,目前在我们淘宝也使用的也非常广泛,现在基本上所有线上业务系统都是部署在Tomcat上。为了对平时开发的Web系统有更深入的理解以及出于好奇心对我们写的Web系统是如何跑在Tomcat上的,于是仔细研究了下Tomcat的源码。大家都知道Servlet规范是Java领域中为服务端编程制定的规范,对于我们开发者只是关注了Servlet规范中提供的编程组件(ServletContextListener,Filer,Servlet) 等 ,但是规范中还有一些我们经常使用的接口(ServletContext,ServletRequest,ServletResponse,FilterChain)等都是由Tomcat去实现的,并且我们开发者实现的编程组件只是被Tomcat去回调而已。所以看Tomcat源码实现也有助于我们更好的理解Servlet规范及系统如何在容器中运行(一些开源的MVC框架如Struts2,Webx,SpringMVC本质无非就是这个),顺便整理了一下与大家分享(Tomcat版本7.0.23,JDK版本1.6)。

二、Tomcat源码目录结构


三、Tomcat体系结构

        仔细查看下图(网络上描述Tomcat架构比较清晰的一张图),不难发现其中的Connecotr组件以及与Container组件是Tomcat的核心。一个Server可以有多个Service,而一个Service可以包含了多个Connector组件和一个Engine容器组件,一个Engine可以由多个虚拟主机Host组成,每一个Host下面又可以由多个Web应用Context构成,每一个的Context下面可以包含多个Wrapper(Servlet的包装器)组成。

       Tomcat将Engine,Host,Context,Wrapper统一抽象成Container。一个抽象的Container模块可以包含各种服务。例如,Manager管理器(Session管理),Pipeline管道( 维护管道阀门Value )等。Lifecycle接口统一定义了容器的生命周期,通过事件机制实现各个容器间的内部通讯。而容器的核心接口Container的抽象实现中定义了一个Pipeline,一个Manager,一个Realm以及ClassLoader统一了具体容器的实现规范。连接器(Connector)组件的主要任务是为其所接收到的每一个请求(可以是HTTP协议,也可以AJP协议),委托给具体相关协议的解析类ProtocolHandler,构造出Request 对象和Response 对象。然后将这两个对象传送给容器(Container)进行处理。容器(Container)组件收到来自连接器(Connector)的Request 和Response对象后,负责调用Filter,最后调用Servlet的service 方法(进入我们开发的Web系统中)。

四、Tomcat源码解析

        Servlet规范由一组用 Java编程语言编写的类和接口组成。Servlet规范为服务端开发人员提供了一个标准的 API以及为服务器厂商制定了相关实现规范,开发人员只需要关心Servlet规范中的编程组件(如Filter,Servlet等),其他规范接口由第三方服务器厂商(如Tomcat)去实现,三者的关系如下图,Servlet规范之于Tomcat的关系,也类似于JDBC规范与数据库驱动的关系,本质就是一套接口和一套实现的关系。对于一个Web服务器主要需要做的事情,个人认为基本由以下组件组成: [TCP连接管理] --> [请求处理线程池管理] --> [HTTP协议解析封装] --> [Servlet规范的实现,对编程组件的回调] --> [MVC框架,Web系统业务逻辑]

       Tomcat的源码及功能点因为实在过于庞大,下面的源码解析不可能全部功能模块都涉及到,接下来我们主要会根据Tomcat的启动、请求处理、关闭三个流程来梳理Tomcat的具体实现,尽可能把各个模块的核心组件都展示出来。 基本上每个流程的源码分析我都画了时序图,如果不想看文字的话,可以对着时序图看(如果想看完整的源代码分析也会在附件中上传),最后还会分析下Tomcat的Connector组件及Tomcat运行过程中的线程概况及线程模型及Tomcat的类加载机制及Tomcat所涉及的设计模式。

                                                              Servlet规范和Web应用及Web容器间的关系                                                                                                                           

                                                           Tomcat对Servlet规范的实现

1. Tomcat的启动流程         

Tomcat在启动时的重点功能如下:

·        初始化类加载器:主要初始化Tomcat加载自身类库的StandardClassLoader。

·        解析配置文件:使用Digester组件解析Tomcat的server.xml,初始化各个组件(包含各个web应用,解析对应的web.xml进行初始化)。

·        初始化Tomcat的各级容器Container,当然最后会初始我们Web应用(我们熟悉的Listener,Filter,Servlet等初始化等在这里完成)。

·        初始化连接器Connector:初始化配置的Connector,以指定的协议打开端口,等待请求。

不管是是通过脚本bootstrap.sh启动还是通过Eclipse中启动,Tomcat的启动流程是在org.apache.catalina.startup.Bootstrap类的main方法中开始的,启动时这个类的核心代码如下所示:

Java代码 

1.  public static void main(String args[]) {  

2.      if (daemon == null) {  

3.           daemon = new Bootstrap(); //实例化Bootstrap的一个实例  

4.           try {

5.               // 创建StandardClassLoader类加载器,并设置为main线程的线程上下文类加载器

6.               // 通过StandardClassLoader加载类Cataline并创建Catalina对象 ,并保存到Boosstrap对象中

7.               daemon.init();

8.           } catch (Throwable t) {  

9.          }  

10.     }  

11.     if (command.equals("start")) {  

12.          daemon.setAwait(true);  

13.           daemon.load(args); //执行load,加载资源,调用Catalinaload方法,利用Digester读取及解析server.xml配置文件并且创建StandardServer服务器对象 */

14.           daemon.start(); //启动各个组件,容器开始启动,调用Catalinastart方法

15.     }  

16. }  

从以上的代码中,可以看到在Tomcat启动的时候,执行了三个关键方法即init、load、和start。后面的两个方法都是通过反射调用org.apache.catalina.startup.Catalina的同名方法完成的,所以后面在介绍时将会直接转到Catalina的同名方法。首先分析一下Bootstrap的init方法,在该方法中将会初始化一些全局的系统属性、初始化类加载器、通过反射得到Catalina实例,在这里我们重点看一下初始化类加载器的initClassLoaders()方法

Java代码 

1.  private void initClassLoaders() {  

2.          try {

3.             //创建StandardClassLoader类加载器  

4.              commonLoader = createClassLoader("common"null);  

5.              if( commonLoader == null ) {  

6.                  commonLoader=this.getClass().getClassLoader();  

7.              }  

8.              catalinaLoader = createClassLoader("server", commonLoader);  

9.              sharedLoader = createClassLoader("shared", commonLoader);  

10.         } catch (Throwable t) {  

11.             System.exit(1);  

12.         }  

13. }  

在以上的代码总,我们可以看到初始化了StandardClassLoader类加载器,这个类加载器详细的会在后面关于Tomcat类加载器机制中介绍。

然后我们进入Catalina的load方法:

Java代码 

1.  public void load() {  

2.          //初始化Digester组件,定义了解析规则  

3.          Digester digester = createStartDigester();  

4.          try {  

5.              inputSource.setByteStream(inputStream);  

6.              digester.push(this);  

7.              //通过Digester解析这个文件,在此过程中会初始化各个组件实例及其依赖关系

8.             //最后会把server.xml文件中的内容解析到StandardServer

9.              digester.parse(inputSource);  

10.             inputStream.close();  

11.         } catch (Exception e) {}  

12.         if (getServer() instanceof Lifecycle) {  

13.             try {

14.                // 调用Serverinitialize方法,初始化各个组件  

15.                 getServer().init();  

16.             } catch (LifecycleException e) {  

17.             }  

18.         }  

19. }  

在以上的代码中,关键的任务有两项即使用Digester组件按照给定的规则解析server.xml、调用Server的init方法,而Server的init方法中,会发布事件并调用各个Service的init方法,从而级联完成各个组件的初始化。每个组件的初始化都是比较有意思的,但是我们限于篇幅先关注Tomcat各级容器的初始化及Connector的初始化,这可能是最值得关注的地方。

首先看下StandardService的start方法(), 核心代码如下:

1.  // StandardService的启动  

2.  protected void startInternal() throws LifecycleException {

4.      //启动容器StandardEngine 

5.      if (container != null) {  

6.          synchronized (container) {  

7.              // 启动StandardEngine容器  

8.              container.start();  

9.          }  

10.     }  

12.     //两个connector的启动,HTTP/1.18080AJP/1.38009  

13.     synchronized (connectors) {  

14.         for (Connector connector: connectors) {  

15.             try {  

16.                 if (connector.getState() != LifecycleState.FAILED) {  

17.                     //依次启动Connector[HTTP/1.1-8080]Connector[AJP/1.3-8009]

18.                     //这里会创建请求线程执行器并启动接收线程Acceptor

19.                     connector.start();  

20.                 }  

21.             } catch (Exception e) {  

22.             }  

23.         }  

24.     }  

25. }  

启动Tomcat各级容器的会依次先启动StandardEngine --> StandardHost --> StandardContext(代表一个WebApp应用), 因为我们比较关心我们的Web应用是哪里被初始化回调的,所以就重点看下StandardContext的start()方法,核心代码如下:

Java代码 

1.  //启动WebApp应用 

2.  @Override  

3.  protected synchronized void startInternal() throws LifecycleException {  

4.      boolean ok = true;  

6.      if (getLoader() == null) {  

7.          //创建此WebApp应用的WebApp载入器WebAppLoader,这个应用的WebAppClassLoader类加载器全部是由这个载入器创建

8.          WebappLoader webappLoader = new WebappLoader(getParentClassLoader());  

9.          webappLoader.setDelegate(getDelegate());  

10.         //在这里开始启动WebAppLoader加载器,并且创建此WebAppWebAppClassLoader类加载器,保存到WebAppLoader,最后会被设置到此ContextInstanceManager

11.         setLoader(webappLoader);  

12.     }  

14.     ///线程上下文类加载器切换成当前WebApp的类加载器,Contextloader中获取 

15.     ClassLoader oldCCL = bindThread();  

17.     try {  

18.         if (ok) {  

19.             //触发Context组件的configure_start事件,通知ContextConfig监听器

20.             //开始解析Web应用WEB-INF/web.xml文件配置到WebXml对象,最后把配置信息全部解析到StandardContext

21.             fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);  

23.             if (pipeline instanceof Lifecycle) {  

24.                 //启动StandardContext的管道Pipeline

25.                 ((Lifecycle) pipeline).start();  

26.             }  

27.         }  

28.     } finally {  

29.         //线程上下文类加载器切换成之前的StandardClassLoader

30.         unbindThread(oldCCL);  

31.     }  

33.     //线程上下文类加载器切换成当前WebApp的类加载器,Contextloader中获取

34.     oldCCL = bindThread();  

36.     if (ok ) {  

37.         if (getInstanceManager() == null) {  

38.             Map<String, Map<String, String>> injectionMap = buildInjectionMap(  

39.                     getIgnoreAnnotations() ? new NamingResources(): getNamingResources());  

40.             //创建每个应用StandardContext自己的DefaultInstanceManager实例管理器对象

41.             //并且会从WebAppLoader拿到WebAppClassLoader类加载器,之后此WebApp的应用类都由此ClassLoader加载

42.             setInstanceManager(new DefaultInstanceManager(context,  

43.                     injectionMap, thisthis.getClass().getClassLoader()));  

44.         }  

45.     }  

47.     try {  

48.         //StandardContext的上下文参数设置到ServletContext

49.         mergeParameters();  

51.         if (ok) {  

52.             //启动监听器Listener

53.             if (!listenerStart()) {  

54.                 log.error( "Error listenerStart");  

55.                 ok = false;  

56.             }  

57.         }  

59.         try {  

60.             if ((manager != null) && (manager instanceof Lifecycle)) {  

61.                 //启动Session管理器StandardManager

62.                 ((Lifecycle) getManager()).start();  

63.             }  

64.             super.threadStart();  

65.         } catch(Exception e) {  

66.             ok = false;  

67.         }  

69.         if (ok) {  

70.             //启动过滤器Filter

71.             if (!filterStart()) {  

72.                 log.error("Error filterStart");  

73.                 ok = false;  

74.             }  

75.         }  

77.         if (ok) {  

78.             //启动load-on-startupServlet

79.             loadOnStartup(findChildren());  

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值