Tomcat源码简单分析

前言

Tomcat是目前应用比较多的servlet容器,有复杂的结构,想要了解它,就应该顺着开发者设计之初的思路来,先了解整体的结构,对整体有了一定的掌控后,再逐个分析,了解感兴趣的细节。

架构设计

简单介绍各个模块:

  • Server:服务器的意思,代表整个tomcat服务器,一个tomcat只有一个Server;

  • Service:Server中的一个逻辑功能层, 一个Server可以包含多个Service;

  • Connector:称作连接器,是Service的核心组件之一,一个Service可以有多个Connector,主要是连接客户端请求;

  • Container:Service的另一个核心组件,按照层级有Engine,Host,Context,Wrapper四种,一个Service只有一个Engine,其主要作用是执行业务逻辑;

server.xml配置文件 

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
    <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>

    <GlobalNamingResources>
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml"/>
    </GlobalNamingResources>

    <Service name="Catalina">
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443"/>
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
        <Engine name="Catalina" defaultHost="localhost">
            <Realm className="org.apache.catalina.realm.LockOutRealm">
                <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                       resourceName="UserDatabase"/>
            </Realm>

            <Host name="localhost" appBase="webapps"
                  unpackWARs="true" autoDeploy="true">
                <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                       prefix="localhost_access_log" suffix=".txt"
                       pattern="%h %l %u %t &quot;%r&quot; %s %b"/>
            </Host>
        </Engine>
    </Service>
</Server>

Tomcat启动流程

启动Tomcat会是运行startup.bat或者startup.sh文件,这两个文件最后都会调用,org.apache.catalina.startup包下面Bootstrap类的main方法。

main方法先实例化了一个Bootstrap实例,接着调用了init方法。init方法是生命周期方法,接着看init的具体实现。

init方法,先初始化了类加载器。initClassLoaders方法具体实现如下:

è¿éåå¾çæè¿°

Tomcat执行的是start操作,调用完init方法后,会执行load方法。

load方法通过反射调用Catalina类的load方法。

è¿éåå¾çæè¿°

load方法中比较重要的方法是createStartDigester(),createStartDigester方法主要的作用就是帮我们实例化了所有的服务组件包括server,service和connect。

初始化操作完成后,接下来会执行catalina实例的start方法。Tomcat会默认加载org.apache.catalina.core.StandardServer作为Server的实例类。

在Server的start的方法里面会执行service的start方法。在createStartDigester()方法里面,会默认加载org.apache.catalina.core.StandardService类。会接着调用Service的start方法。

è¿éåå¾çæè¿°

service中会调用connector的start方法。至此Tomcat启动完毕

Tomcat运行流程

假设来自客户的请求为:http://localhost:8080/test/index.jsp.请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得,然后

  • Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的回应

  • Engine获得请求localhost:8080/test/index.jsp,匹配它所有虚拟主机Host

  • Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)

  • localhost Host获得请求/test/index.jsp,匹配它所拥有的所有Context

  • Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为""的Context去处理)

  • path="/test"的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet

  • Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类,构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法

  • Context把执行完了之后的HttpServletResponse对象返回给Host

  • Host把HttpServletResponse对象返回给Engine

  • Engine把HttpServletResponse对象返回给Connector

  • Connector把HttpServletResponse对象返回给客户browser

 Server

Server是Tomcat最顶层的容器,代表着整个服务器,即一个Tomcat只有一个Server,Server中包含至少一个Service组件,用于提供具体服务。这个在配置文件中也得到很好的体现(port=”8005” shutdown=”SHUTDOWN”是在8005端口监听到”SHUTDOWN”命令,服务器就会停止)。

Tomcat中其标准实现是:org.apache.catalina.core.StandardServer类,继承LifecycleMBeanBase,,tomcat为所有的组件都提供了生命周期管理。

Service

在conf/server.xml文件中,可以看到Service组件包含了Connector组件和Engine组件(前面有提过,Engine就是一种容器),即Service相当于Connector和Engine组件的包装器,将一个或者多个Connector和一个Engine建立关联关系。在默认的配置文件中,定义了一个叫Catalina 的服务,它将HTTP/1.1和AJP/1.3这两个Connector与一个名为Catalina 的Engine关联起来。

一个Server可以包含多个Service(它们相互独立,只是公用一个JVM及类库),一个Service负责维护多个Connector和一个Container。其标准实现是StandardService。

Connector

Connector主要负责处理与客户端的通信,Connector的实例用于监听端口,接受来自客户端的请求并将请求转交给Engine处理,同时将来自Engine的答复返回给客户端。

connector执行流程

Tomcat源码中与connector相关的类位于org.apache.coyote包中,Connector分为以下几类:

Http Connector, 基于HTTP协议,负责建立HTTP连接。它又分为BIO Http Connector与NIO Http Connector两种,后者提供非阻塞IO与长连接Comet支持。默认情况下,Tomcat使用的就是这个Connector。

AJP Connector, 基于AJP协议,AJP是专门设计用来为tomcat与http服务器之间通信专门定制的协议,能提供较高的通信速度和效率。如与Apache服务器集成时,采用这个协议。

APR HTTP Connector, 用C实现,通过JNI调用的。主要提升对静态资源(如HTML、图片、CSS、JS等)的访问性能。现在这个库已独立出来可用在任何项目中。Tomcat在配置APR之后性能非常强劲。

Connector使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型,比如:Http11Protocol使用的是普通Socket来连接的(tomcat9已经删除了这个类,不再采用BIO的方式),Http11NioProtocol使用的是NioSocket来连接的。

其中ProtocolHandler由包含了三个部件:Endpoint、Processor、Adapter。 
1. Endpoint用来处理底层Socket的网络连接,Processor用于将Endpoint接收到的Socket封装成Request(这个Request和ServletRequest无关),Adapter充当适配器,用于将Request转换为ServletRequest交给Container进行具体的处理。 
2. Endpoint由于是处理底层的Socket网络连接,因此Endpoint是用来实现TCP/IP协议的,而Processor用来实现HTTP协议的,Adapter将请求适配到Servlet容器进行具体的处理。 
3. Endpoint的抽象实现AbstractEndpoint里面定义的Acceptor和AsyncTimeout两个内部类和一个Handler接口。Acceptor用于监听请求,AsyncTimeout用于检查异步Request的超时,Handler用于处理接收到的Socket,在内部调用Processor进行处理。

下面我们以org.apache.coyote.http11.Http11AprProtocol为例说明Connector的工作流程。

①它将工作委托给AprEndpoint类。

public Http11AprProtocol() {  
        endpoint = new AprEndpoint();     // 主要工作由AprEndpoint来完成  
        cHandler = new Http11ConnectionHandler(this);    // inner class  
        ((AprEndpoint) endpoint).setHandler(cHandler);

②在AprEndpoint.Acceptor类中的run()方法会接收一个客户端新的连接请求.

protected class Acceptor extends AbstractEndpoint.Acceptor {  
  
    private final Log log = LogFactory.getLog(AprEndpoint.Acceptor.class);  
  
    @Override  
    public void run() {  
  
        int errorDelay = 0;  
  
        // Loop until we receive a shutdown command  
        while (running) {  

③在AprEndpoint类中,有一个内部接口Handler,该接口定义如下:

public interface Handler extends AbstractEndpoint.Handler {  
        public SocketState process(SocketWrapper<Long> socket,  
                SocketStatus status);  
    }

④在Http11AprProtocol类中实现了AprEndpoint中的Handler接口,

protected static class Http11ConnectionHandler  
           extends AbstractConnectionHandler<Long,Http11AprProcessor> implements Handler {  


并调用Http11AprProcessor类(该类实现了ActionHook回调接口)。

protected Http11AprProcessor createProcessor() {  
            Http11AprProcessor processor = new Http11AprProcessor(

Container 

Tomcat提供了一个Container接口来抽象容器,并且细分了4种类型的容器,分别是Engine、Host、Context和Wrapper,对应不同的概念层次。

· Engine:表示整个Catalina的servlet引擎

· Host:表示一个拥有数个上下文的虚拟主机

· Context:表示一个Web应用,一个context包含一个或多个wrapper

· Wrapper:表示一个独立的servlet 

         

猜你喜欢

转载自blog.csdn.net/qq_34462387/article/details/81712456