欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Tomcat 架构

程序员文章站 2022-05-23 14:19:47
...

最近在开发过程中,遇到需要对Tomcat配置进行优化,索性把整个Tomcat架构以及底层的一些基础知识一起研究了一下。对于Tomcat的架构设计有很多地方值得借鉴学习,包括模块化设计,模块组件的生命周期管理,通信编程的一些原理以及技巧等等

一、Tomcat源码导入

研究的Tomcat为:9.0.6,JDK为1.8
IDEA新建Maven工程,我新建的工程为tomcatSource,配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.invoker.fy</groupId>
    <artifactId>tomcatSource</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant-apache-log4j</artifactId>
            <version>1.6.5</version>
        </dependency>
        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant-commons-logging</artifactId>
            <version>1.6.5</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.rpc</groupId>
            <artifactId>javax.xml.rpc-api</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.5.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>tomcatSource</finalName>
        <sourceDirectory>java</sourceDirectory>
        <resources>
            <resource>
                <directory>java</directory>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Apache 官网下载源码:https://tomcat.apache.org/download-90.cgi,下载zip包

Tomcat 架构

解压,将文件copy到工程tomcatSource目录下,工程目录结构如下
Tomcat 架构


Tomcat组件生命周期

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">
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>

    <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实际上是一个servlet容器,从server.xml文件解析可以看出容器包含server、service、connector、container等等,这些容器都具有新建、初始化完成、启动、停止、失败、销毁等状态。Tomcat的实现提供了对这些容器的生命周期管理

Tomcat 架构图

先来看一下Tomcat的顶层架构,Server是最顶层的容器,1个Server包含至少一个Service,1个Service包含多个Connector和1个Container容器
(1)Server控制这个Tomcat的生命周期
(2)Service对外提供服务
(3)Connector用于接受请求并将请求封装成request和response来具体处理
(4)Container用来处理Servlet,具体处理request请求
Tomcat 架构
整个Tomcat最核心的2个容器组件为Connector和Container,开发过程中tomcat调优也在Connector容器组件的配置优化,所以重点说一下Connector容器
Tomcat 架构

一般接收到一个请求时,首先经过Serveice容器,再经过Connector容器,将接收到的请求底层的socket封装成request和resposne来具体处理,然后交由Container处理,container处理完之后返回给Connector,Connector通过socket将处理的结果返回给客户端;这样整个请求就处理完了
(1) Connector就是使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型;ProtocolHandler包含三个组件,endpoint用于处理底层socket网络连接,实现的是TCP/IP协议,Processor用于将Endpoint接收到的Socket封装成Request,Adapter用于将Request交给Container进行具体的处理
(2)Endpoint的抽象实现AbstractEndpoint里面定义的Acceptor和AsyncTimeout两个内部类和一个Handler接口。Acceptor用于监听请求,AsyncTimeout用于检查异步Request的超时,Handler用于处理接收到的Socket,在内部调用Processor进行处理
(3)Connector在接收到请求后会首先调用最顶层容器的Pipeline来处理,这里的最顶层容器的Pipeline就是EnginePipeline,然后执行StandardEngineValue、StandardHostValue、StandardWrapperValue,当执行到StandardWrapperValue,会调用FilterChain处理链,最终执行处理链的service方法;当所有的Pipeline-Value都执行完之后,并且处理完了具体的请求,这个时候就可以将返回的结果交给Connector了,Connector在通过Socket的方式将结果返回给客户端

至此,整个Tomcat架构有了一个清晰的认识,由于tomcat是一个servlet容器,所以每个容器都有servlet特征,包括初始化、service、销毁等方法;代码细节可以研究源码;下面重点说一下tomcat调优,connctor的配置

在项目中,对于web项目war包部署,通常在server.xml中配置Host节点以及war包项目路径,对于Connector配置说一下

    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />

配置中Executor用来配置tomcat共享线程池,配置参数官网说明:
Tomcat 架构
当tomcat启动的时候,一旦有请求过来,就会创建minSpareThreads线程,用来处理请求,当超过maxThreads数量时,进入队列,队列大小控制为maxQueueSize,超过队列数量,就会拒绝请请求;对于这几个参数的设置,要根据业务场景以及服务器性能来决定,如果是计算密集型的,通常maxThreads要控制小一些,因为太大,线程的切换消耗CPU,但IO密集型的,可以控制大一些,提高并发能力;maxThreads默认为200,minSpareThreads默认为25,队列默认为LinkedBlockingQueue*阻塞队列,但不建议设置*,因为任何请求都会有超时时间的设置,可以根据业务场景合理设定;经验值:并发线程数量=CPU核心数 * CPU使用率 * (1 + 等待时间/计算时间),比如CPU核数为64,使用率为0.75,等待时间/计算时间为2,maxThreads设置为144;
注意:如果connector节点配置了Executor,则Executor节点一定要配置在Connector节点前面,一旦配置了Executor,则connector配置的maxThreads就无效
下面来看一下Connector节点配置参数官网说明:http://tomcat.apache.org/tomcat-9.0-doc/config/http.html
Tomcat 架构
重点说一下maxConnections,最大可以接受的连接数,不一定处理;好比看房,取号的都在售楼处外面候着,maxConnections为最大取号的数量,maxThreads为楼房数量,默认一个楼房只允许一个人看(怎么可能!!!)因为tomcat一个线程只会处理一个连接,acceptCount即队列数量,就是售楼处中待看房的人;所以tomcat性能瓶颈依赖的是maxThreads + acceptCount;

上一篇: HashMap源码分析

下一篇: tomcat架构