Tomcat 架构设计

Tomcat 架构设计之源码

Posted by 月光下的海 on January 17, 2022

Tomcat 架构设计

启动

org.apache.catalina.startup.Bootstrap#main()
  • org.apache.catalina.startup.Bootstrap#init()

    • org.apache.catalina.startup.Bootstrap#initClassLoaders
      • 创建三个classLoader, 名称分别为:common、server、shared
    • 设置当前线程上下文的classLoader -Thread.currentThread().setContextClassLoader(catalinaLoader)
    • 设置当前线程上下文的classLoader -Thread.currentThread().setContextClassLoader(catalinaLoader)
    • 反射调用org.apache.catalina.startup.Catalina#setParentClassLoader方法
  • 设置标志位org.apache.catalina.startup.Bootstrap#setAwait(true)

  • 执行org.apache.catalina.startup.Bootstrap#load()方法

    • 反射调用org.apache.catalina.startup.Catalina.load()方法

      • 初始化目录org.apache.catalina.startup.Catalina#initDirs

      • 初始化命名org.apache.catalina.startup.Catalina#initNaming

      • 创建digester,用于解析xml文件。org.apache.catalina.startup.Catalina#createStartDigester

      • 解析web.xml文件

      • 通过模板方法org.apache.catalina.util.LifecycleBase#init ,依次调用org.apache.catalina.core.StandardServer#initInternal

        org.apache.catalina.core.StandardService#initInternal

        • mapperListener.init(); -这个mapperListener具体还没看到是干什么的
        • 会创建两个Connector:Connector[HTTP/1.1-8080]、Connector[AJP/1.3-8009]
        • org.apache.catalina.connector.Connector#initInternal()
          • 创建org.apache.catalina.connector.CoyoteAdapter
          • 调用org.apache.coyote.http11.AbstractHttp11Protocol#init
            • 调用org.apache.tomcat.util.net.AbstractEndpoint#init

        org.apache.catalina.core.StandardEngine#initInternal

        ​ org.apache.catalina.core.ContainerBase#initInternal : 初始化线程池 startStopExecutor

  • 调用org.apache.catalina.startup.Bootstrap#start方法

    • 反射调用org.apache.catalina.startup.Catalina#start方法
      • 通过模板方法org.apache.catalina.util.LifecycleBase#start
        • 执行org.apache.catalina.core.StandardServer#startInternal
        • 执行org.apache.catalina.core.StandardService#startInternal
        • 执行org.apache.catalina.core.StandardEngine#startInternal
        • 执行org.apache.catalina.core.StandardHost#startInternal
        • 执行org.apache.catalina.core.StandardPipeline#startInternal
        • 执行org.apache.catalina.core.ContainerBase#threadStart -启动用来定期检查会话超时的后台线程
        • 执行org.apache.catalina.connector.Connector#startInternal
        • 执行org.apache.coyote.AbstractProtocol#start
        • 执行org.apache.tomcat.util.net.AbstractEndpoint#start
        • 注册 shutdown hook
        • 调用org.apache.catalina.core.StandardServer#await方法 : 为了阻塞主线程,让服务进入等待状态(用来监听HTTP请求的是守护线程)

Servlet/filter定义的获取

在start方法时进行事件的分发,调用org.apache.catalina.util.LifecycleBase#fireLifecycleEvent

  • org.apache.catalina.startup.ContextConfig#lifecycleEvent
    • org.apache.catalina.startup.ContextConfig#configureStart
      • org.apache.catalina.startup.ContextConfig#webConfig -
        • org.apache.tomcat.util.descriptor.web.WebXmlParser#parseWebXml(org.xml.sax.InputSource, org.apache.tomcat.util.descriptor.web.WebXml, boolean) : 解析Web.xml文件 ,并将解析后的信息保存在org.apache.tomcat.util.descriptor.web.WebXml实例中去(可以参考org.apache.tomcat.util.digester.Digester解析xml的过程- 使用Digester的push方法在Digester使用的stack中放置一个初始对象。在解析xml文件的过程中,Digester使用stack来保存它所找到的对象。第一个对象在遇到第一个标签时被放置到stack中,当最后一个标签处理完毕时被弹出。为了最后能检索到这个对象,会用一个初始对象来保留一个指向它的引用)
        • 解析完毕之后会调用org.apache.catalina.startup.ContextConfig#configureContext方法将org.apache.tomcat.util.descriptor.web.WebXml对象中的信息设置到对象中去
          • 比如org.apache.catalina.Context#setSessionTimeout
          • 获取servlet的映射关系,关联到context对象中org.apache.catalina.Context#addServletMapping(java.lang.String, java.lang.String)
        • 在servlet3.0+ 后,也可以用注解的方式javax.servlet.annotation.WebServlet,javax.servlet.annotation.WebFilter代替web.xml文件中的配置,所以这些数据的来源除了要解析web.xml,还有对于注解配置的处理(从类中定义的注解获取servlet/filter的相关定义)
          • org.apache.catalina.startup.ContextConfig#processAnnotationWebServlet
          • org.apache.catalina.startup.ContextConfig#processAnnotationWebFilter

Servlet的实例化/初始化

  • org.apache.catalina.core.StandardContext#startInternal

    • org.apache.catalina.core.StandardContext#loadOnStartup -根据load-on-startup参数实例化所有需要在启动时加载的servlet

      • org.apache.catalina.core.StandardWrapper#load

        • org.apache.catalina.core.StandardWrapper#loadServlet - 实例化(基于反射创建)

          InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
          servlet = (Servlet) instanceManager.newInstance(servletClass);
          
        • org.apache.catalina.core.StandardWrapper#initServlet - 初始化

Tomcat 接收解析HTTP请求

  • org.apache.catalina.connector.Connector#startInternal

    • org.apache.coyote.ProtocolHandler#start

      • org.apache.coyote.ProtocolHandler#start

        • org.apache.tomcat.util.net.AbstractEndpoint#start

          • org.apache.tomcat.util.net.NioEndpoint#startInternal

            • org.apache.tomcat.util.net.NioEndpoint.Acceptor#run

              • org.apache.tomcat.util.net.NioEndpoint.Poller#register

                其中NioEndpoint中的Acceptor负责监听TCP/IP连接,并且通过调用run方法,将获取到的SocketChannel包装成Tomcat中的NioChannel,并注册到Tomcat 自己封装的轮训器Poller中

              • org.apache.tomcat.util.net.NioEndpoint.Poller#run - 轮训器。负责将套接字添加到轮询器的后台线程,检查轮询器是否有触发事件,并在事件发生时将关联的套接字交给适当的处理器。

                • 调用org.apache.tomcat.util.net.NioEndpoint.Poller#processKey

                  • 调用org.apache.tomcat.util.net.NioEndpoint#processSocket

                    • 调用org.apache.tomcat.util.net.NioEndpoint.SocketProcessor#run - worker线程

                      • 调用org.apache.tomcat.util.net.AbstractEndpoint.Handler#process

                        • 调用org.apache.coyote.http11.Http11Processor#process - 处理HTTP请求。其中包括解析请求行:1.org.apache.coyote.http11.Http11InputBuffer#parseRequestLine

                          2.org.apache.coyote.http11.Http11InputBuffer#parseHeaders -解析请求头

                          3 org.apache.coyote.http11.Http11Processor#prepareRequest - 设置请求的过滤器

  • 第一版写的比较粗糙,后续考虑再梳理下流程,使用ProcessOn 绘制流程图出来。
  • 未完,待续。