Tomcat03——Tomcat架构

1. HTTP工作原理

        Http协议是浏览器与服务器之间的数据传输协议,作为应用层协议,http是基于TCP/IP协议来传递数据(HTML文件,图片,查询结果等)的,HTTP协议下不涉及数据包(Package)传输,主要规定了客户端和服务器之间的通信格式。

请求过程如图:

1. 用户在浏览器输入网址,浏览器获取该事件

2. 浏览器接着向服务端发送TCP连接请求

3. 服务程序接受浏览器的请求,并经过TCP的三次握手建立连接

4. 浏览器将请求数据打包成一个HTTP协议的数据包

5. 浏览器将该数据包推入网络,数据包经过网络传输,最终达到服务端

6. 服务端拿到这个数据包之后,同样以HTTP协议格式解包,获取到客户端的意图

7. 得知客户端的意图之后,进行处理,比如提供静态文件或者调用服务端程序获取动态资源

8. 服务器将响应结果(HTML、图片等)按照HTTP协议格式打包

9. 服务器将响应数据包推入网络,数据包经过网络传输最终到达浏览器

10. 浏览器拿到数据包之后,以HTTP协议的格式解包,然后解析数据,假设这里的数据是HTML

11. 浏览器将HTML文件展示在页面上

从中可以看出,服务器主要是接受连接,解析请求数据,处理请求和发送响应这几个步骤。

2. Tomcat的整体架构

2.1 HTTP服务器请求处理

        浏览器给服务端发送一个HTTP格式的请求,HTTP服务器接收到这个请求后,需要调用服务端程序来处理,所谓的服务端程序就是我们写的java代码,一般来说不同的请求,需要有不同的java类来处理。

        HTTP服务器不直接调用业务类,而是把请求交给容器来处理,容器通过Servlet接口调用业务类,因此Servlet接口和Servlet容器的出现,达到了HTTP服务器与业务类解耦合的目的。

        Servlet接口和Servlet容器这一整套规范就叫做Servlet规范。Tomcat按照Servlet规范的要求实现了Servlet容器,同时它们也具有HTTP服务器的功能。作为Java程序员,如果我们要实现新的业务功能,只需要实现一个Servlet,并把它注册到Tomcat(Servlet容器)中,剩下的事情,就由Tomcat来处理。

如图:

2.2 Servlet容器的工作流程

        为了解耦,HTTP服务器不直接调用Servlet,而是通过Servlet容器来处理,接下来我们来看看Servlet容器的工作流程是怎么样的。

        当客户请求某个资源时,HTTP服务器会用一个ServletRequest对象把客户的请求信息封装起来,然后调用Servlet容器的service方法,Servlet容器拿到请求之后,根据请求的URL和Servlet的映射关系,找到相应的Servlet,如果Servlet还没有被加载,就用反射机制创建这个Servlet,并调用Servlet的init方法来完成Servlet的初始化,接着调用Servlet的service方法来处理请求,把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端。

如图:

2.3 Tomcat整体架构

tomcat要实现的两个核心功能:

1. 处理socket连接,负责网络字节流与Request和Response对象的转化。

2. 加载和管理Servlet,以及具体处理Request请求。

因此Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。连接器负责对外交流,容器负责内部处理。

如图:

3. 连接器 — Coyote

3.1 架构介绍

        Coyote是Tomcat的连接器框架的名称,是Tomcat服务器提供的供客户端访问的外部接口,客户端通过Coyote与服务器建立连接,发送请求并接受响应。

        Coyote封装了底层的网络通信(Socket请求及响应处理),为Catalina容器提供了统一的接口,使Catalina容器与具体的请求协议及IO操作方式完全解耦。Coyote将Socket输入转换封装为Request对象,交由Catalina容器进行处理,处理请求完成之后,Catalina通过Coyote提供的Response对象将结果写入输出流。

        Coyote作为独立的模块,只负责具体协议及IO的相关操作,与Servlet规范实现没有直接关系,因此即便是Request和Response对象也并没有实现Servlet规范对应的接口,而是Catalina中将他们进一步封装为ServletRequest和ServletResponse。

如图:

3.2 IO模型与协议

        在Coyote中,Tomcat支持的多种I/O模型和应用层协议,具体包含哪些IO模型和应用层协议,如下表所示:

Tomcat支持的IO模型(自8.5/9.0版本起,Tomcat移除了对BIO的支持)

IO模型 描述
NIO 非阻塞I/O,采用Java NIO类库实现
NIO2 异步I/O,采用JDK 7最新的NIO2类库实现
APR 采用Apache可移植运行库实现,是C/C++编写的本地库,如果选择该方案,需要单独安装APR库

Tomcat支持的应用层协议:

应用层协议 描述
HTTP/1.1 这是大部分WEB应用访问的协议
AJP 用于和WEB服务器集成(如:Apache),以实现对静态资源的优化以及集群部署,当前支持AJP/1.3
HTTP/2 HTTP 2.0大幅度的提升了Web性能,下一代的HTTP协议,自8.5以及9.0之后开始支持

协议分层:

应用层 HTTP AJP(Processor) HTTP 2
传输层 NIO NIO2(Endpoint) APR

        在8.0 之前,Tomcat默认采用的I/O方式为BIO,之后改成了NIO,无论是NIO、NIO2还是APR,在性能方面都要优于BIO,如果采用APR,甚至可以达到Apache HTTP Server 的影响性能。

        Tomcat为了实现支持多种I/O模型和应用层协议,一个容器可以对接多个连接器,但是单独的连接器或者容器都不能对外提供服务,需要把他们组装起来才能工作,组装后的这个操作叫做Service组件。值得注意的是,Service本身没有做什么重要的事情,只是在连接器和容器外面多包了一层,把他们组装在一起,Tomcat内可能有多个Service,这样的设计也是出于灵活性的考虑,通过在Tomcat中配置多个Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。

3.3 连接组件

如图:

3.3.1 EndPoint

        Coyote通信端点,即通信监听的接口,是具体的Socket接收和发送处理器,是对传输层的抽象,因此EndPoint用来实现TCP/IP协议。

        Tomcat并没有EndPoint接口,而是提供了一个抽象类AbstractEndpoint,里面定义了两个内部类:Acceptor和SocketProcessor。

1. Acceptor 用于监听Socket连接请求。

2. SocketProcessor 用于处理接收到的Socket请求,它实现了Runnable接口,在run方法中调用协议处理组件processor进行处理。为了提高处理能力,socketProcessor被提交到线程池来执行,而这个线程池叫做执行器(Executor)。

3.3.2 Processor

        Coyote处理协议的接口,如果说EndPoint是用来实现TCP/IP协议的,那么Processor用来实现HTTP协议,其接收来自EndPoint的Socket,读取字节流,解析成Tomcat Request和Response对象,并通过Adapter将其提交到容器处理,Processor是对应用层协议的抽象。

3.3.3 ProtocolHandler

        Coyote协议接口,通过Endpoint和Processor实现针对具体协议的处理能力。Tomcat按照协议和I/O提供了6个实现类:

AjpNioProtocol、AjpAprProtocol、AjpNio2Protocol、Http11NioProtocol、Http11Nio2Protocol、Http11AprProtocol。

        我们在配置tomcat/conf/server.xml时,至少要指定具体的ProtocolHandler,当然也可以指定协议名称,例如:HTTP/1.1,如果安装了APR,那么将使用Http11AprProtocol,否则使用Http11NioProtocol。

3.3.4 Adaptor

        由于协议的不同,客户端发过来的请求信息也不尽相同,Tomcat定义了自己的Request类来存放请求信息。ProtocolHandler接口负责解析请求并生成Tomcat Request类,但是这个Request对象不是标准的ServletRequest,也就意味着不能用Tomcat Request作为参数调用容器。

        Tomcat设计者的解决方案是引入CoyoteAdapter,这是适配器模式的经典应用,连接器调用CoyoteAdapter的Service方法,传入的是Tomcat Request对象,CoyoteAdapter负责将Tomcat Request对象转成ServletRequest,在调用容器的Service方法。

4. Tomcat服务器配置

        tomcat服务器的配置主要集中于tomcat/conf下的catalina.policy、catalina.properties、context.xml、server.xml、tomcat-users.xml、web.xml文件。

4.1 server.xml

       是tomcat服务器的核心配置文件,包含了Tomcat的Servlet容器(Catalina)的所有配置。

如下所示,为下载之后的原版配置文件

<?xml version='1.0' encoding='utf-8'?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!-- Note:  A "Server" is not itself a "Container", so you may not
     define subcomponents such as "Valves" at this level.
     Documentation at /docs/config/server.html
 -->
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <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>

  <!-- A "Service" is a collection of one or more "Connectors" that share
       a single "Container" Note:  A "Service" is not itself a "Container",
       so you may not define subcomponents such as "Valves" at this level.
       Documentation at /docs/config/service.html
   -->
  <Service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->


    <!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
    -->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <!-- A "Connector" using the shared thread pool-->
    <!--
    <Connector executor="tomcatThreadPool"
               port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    -->
    <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443
         This connector uses the NIO implementation that requires the JSSE
         style configuration. When using the APR/native implementation, the
         OpenSSL style configuration is required as described in the APR/native
         documentation -->
    <!--
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" />
    -->

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />


    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).
         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine name="Catalina" defaultHost="localhost">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <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>

4.1.1 Server

        是server.xml的根元素,用于创建一个Server实例,默认使用的实现类是 org.apache.catalina.core.StandardServer。

<Server port="8005" shutdown="SHUTDOWN">
</Server>

port:tomcat监听的关闭服务器端口。

shutdown:关闭服务器的指令字符串。

Server内嵌的子元素为Listener、GlobalNamingResources、Service

1. Listener

默认配置的5个Listener的含义:

<!-- 用于以日志形式输出服务器、操作系统、JVM版本信息 -->
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  
  <!-- 用于加载(服务器启动)和销毁(服务器停止)APR。如果找不到APR类,则会输出日志,并不影响Tomcat启动 -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  
  <!-- 用于避免JRE内存泄漏问题 -->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  
  <!-- 用于加载(服务器启动)和销毁(服务器停止)全局命名服务 -->
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  
  <!-- 用于在Context停止时重建Executor池中的线程,以避免ThreadLocal相关的内存泄漏 -->
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

2. GlobalNamingResources

        <GlobalNamingResources>.中定义了全局命名资源

3. Service

        <Service>标签用于创建Service实例,默认使用的是org.apache.catalina.core.StandardService,默认情况下,Tomcat仅指定了Service的名称,值为“ Catalina ”。

        Service可以内嵌的元素有:Listener、Executor、Connector、Engine

3.1 Listener

        用于为Servlet添加生命周期监听器。

3.2 Executor

        用于配置Service共享线程池

3.3 Connector

        用于配置Service包含的链接器

3.4 Engine

        用于配置Service中链接器对应的Servlet容器引擎

<Service name="Catalina">
</Service >

一个Server服务器,可以包含多个Service。

5. Tomcat的启动流程

5.1 流程

步骤,如图:

1. 启动tomcat,需要调用bin/startup.bat,在startup.bat脚本中,调用了catalina.bat

2. 在catalina.bat脚本文件中,调用了BootStrap中的main方法

3. 在BootStrap的main方法中调用了init方法,来创建Catalina及初始化类加载器

4. 在BootStrap的main方法中调用load方法,在其中又调用了Catalina的load方法

5. 在Catalina的load方法中,需要进一步初始化的工作,并需要构造Digester对象,用于解析XML

6. 然后调用后续组件的初始化工作。

一句话来描述就,加载tomcat的配置文件,初始化容器组件,监听对应的端口号,准备接受客户端的请求。

5.2 源码解析

5.2.1 Lifecycle接口

        由于所有的组件均存在初始化,启动,停止等生命周期管理的特性,所以Tomcat在设计的时候,基于生命周期管理抽象成了一个接口Lifecycle,而组件Server、Service、Container、Executor、Connector组件,都实现了一个生命周期的接口,从而具有了以下生命周期中的核心方法

1. init():初始化组件

2. start():启动组件

3. stop():停止组件

4. destroy():销毁组件

如图:

5.1.2  各组件的默认实现

        像Server、Service、Engine、Host、Context都是接口,所以,tomcat一定对这些接口有对应的实现类。

        当前对于组件Endpoint来说,在tomcat中没有对应的Endpoint接口,但是有一个抽象类AbstractEndpoint,其下有三个实现类:NioEndpoint、Nio2Endpoint、AprEndpoint,这三个实现类,分别对应于连接器Coyote的三种IO模型:NIO、NIO2、APR,在tomcat8.5版本中默认采用的是NioEndpoint。
如图:

下面罗列一下各个接口的默认实现类:

Server——>StandardServer

Service——>StandardService

Engine——>StandardEngine

Host——>StandardHost

Context——>StandardContext

另外还有一个比较重要的接口是ProtocolHandler,它是Coyote协议接口,通过封装Endpoint和Processor,实现针对具体协议的处理能力。tomcat按照协议和IO提供了6个实现类:

AJP协议:

AjpNioProtocol 采用NIO的IO模型
AjpNio2Protocol 采用NIO2的IO模型
AjpAprProtocol 采用APR的IO模型,需要依赖APR库

在Server.xml中的配置如下:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

 HTTP协议:

Http11NioProtocol 采用NIO的IO模型,默认使用的协议(如果服务器没有安装APR)
Http11Nio2Protocol 采用NIO2的IO模型
Http11AprProtocol 采用APR的IO模型,需要依赖与APR库

 在Server.xml中配置如下:

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />

如图:

发布了128 篇原创文章 · 获赞 6 · 访问量 3222

猜你喜欢

转载自blog.csdn.net/weixin_43318134/article/details/103915555
今日推荐