百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Tomcat原理剖析及性能调优

nanshan 2024-11-24 19:36 19 浏览 0 评论

1 Tomcat的原理和配置文件详解


1.1 tomcat服务器总体架构


tomcat服务器具备两种能力,一种是作为一个Http服务器,另外一个则是作为Servlet容器,两大功能将tomcat拆分为两个组件,Coyote(连接器)与Catalina(容器)两个组件。其中Coyote负责处理Socket通讯,将Socket解析封装为Request对象(非Java ee的Request对象),Catalina负责Servlet初始化、加载等。


1.2 Tomcat连接器组件Coyote

在进行Coyote组件的分析之前,我们先来看下Coyote组件的职能:

1)处理socket请求

2)与Catalina组件解耦

3)将请求封装成request对象,并将该对象转发给Catalina组件封装成HttpServletRequst对象

4)负责网络层和传输层的内容

由上面我们可以知道,Tomcat连接器组件主要是处理TCP/IP模型下的网络层和传输层的内容,具体的TCP/IP模型可参考如下图。


Tomcat主要是职责是处理TCP/IP模型中的传输层和应用层,其中应用层支持的协议有HTTP(1.1与2.0版本,默认为1.1版本),传输层支持NIO和ARP两种传输模型,这里具体的I/O传输模型这里不多做分析。

1.3Coyote内部组件功能

Coyote组件

功能

Adapter

位于org.apache.coyote包中,Tomcat中的Catalina组件入口,将tomcat的request和response 转换成ServletRequest和HttpResponse,并用Catalina容器

Processor

所有协议处理器的通用接口(应用层协议),加工处理Socket连接并封装成tomcat Request和Response对象,并通过Adapter传入容器中

ProtocolHandler

Tomcat协议接口,定义具体协议的处理能力。

EndPoint

Coyote通讯端点,监听Socket请求,向Processor提供字节流

发起请求------》EndPoint------提供字节流----->Processor---提供res和rep对象----》Adapter --提供ServletRequest、ServletResponse--》Catalina容器。

1.4 Tomcat Catalina组件

Tomcat的核心组件,可以认为所有的组件都是为Catalina组件服务,Catalina是一个Servlet容器。Tomcat启动都会示例化一个Catalina实例,Catalina实例会读取conf/server.xml这个配置文件完成其他组件的实例创建。查看Tomcat的conf/server.xml文件,Catalina 可以实例化一个Server容器和多个Service容器。Service下面可以配置多个Connector组件绑定到一个Container容器。


我们读配置文件,可看到Catalina容器组件可由具体的以下几种容器组件构成。

Engine: Catalina的引擎,在Service下只能有一个Engine

Host:一个站点,位于Engine下面,可以配置多个虚拟站点地址,可以包含多个Content

Content:一个站点程序,位于Host下面,可以包含多个Wrapper

Wrapper:一个Wrapper代表一个Servlet实例,容器最底层,不在包含其他容器

1.5Tomcat配置文件详解

Tomcat核心配置文件位于conf/server.xml。


<?xml version="1.0" encoding="UTF-8"?>
<!-- 
 根节点
 port:为关闭服务的监听端口
 shutdown:关闭服务的指令
 -->
<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加载以及管理 -->
  <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-->
    <!--
    service共享线程池
    name: 线程池名称,在Connector可指定使用的共享线程池名称
    namePrefix:创建的线程池名称前缀,线程名称为namePrefix+threadNumber
    maxThreads:最大线程池数量
    minSpareThreads:活跃线程数量,线程不会被销毁
    maxIdlTime:线程空闲时间,超过该时间销毁空线程,默认6000
    maxQueueSize: 线程池最大排队数量,默认值为int最大值。超过这个值tomcat不在处理请求
    prestartminSpareThreads:启动线程池时是否启动 minSpareThreads部分线程。默认值为false,即不启动
    threadPriority:线程池中线程优先级,默认值为5,值从1到10
    className:线程池实现类,未指定情况下,默认实现类为org.apache.catalina.core.StandardThreadExecutor。如果想使??定义线程池?先需要实现org.apache.catalina.Executor接?
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->
    <Executor name="commonThreadPool"
              namePrefix="thread-exec-"
              maxThreads="200"
              minSpareThreads="100"
              maxIdleTime="60000"
              maxQueueSize="Integer.MAX_VALUE"
              prestartminSpareThreads="false"
              threadPriority="5"
              className="org.apache.catalina.core.StandardThreadExecutor"/>


    <!--
    port:端?号,可设置为0,Tomcat随机拿一个可以使用的端口给Connector使用。
    protocol: 访问协议。 默认为 HTTP/1.1 , 并采??动切换机制选择?个基于 JAVA
    NIO 的链接器或者基于本地APR的链接器(根据本地是否含有Tomcat的本地库判定)
    connectionTimeOut: 请求超时时间, 单位为 毫秒。 -1 表示不超时。
    redirectPort:HTTPS默认端口
    executor:制定使用线程池
    Engine 标签
    Engine 表示 Servlet 引擎
    Host 标签
    Host 标签?于配置?个虚拟主机
    URIEncoding:
    ?于指定编码URI的字符编码, Tomcat8.x版本默认的编码为 UTF-8 , Tomcat7.x版本默认为ISO-
    8859-1
    -->
    <!--org.apache.coyote.http11.Http11NioProtocol , ?阻塞式 Java NIO 链接器-->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <!-- 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">
      
        <Context docBase="/webapps/demo" path="/demo"></Context>
        <!-- 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 "%r" %s %b" />

      </Host>
    </Engine>
  </Service>
</Server>

2 Tomcat如何处理一个请求(源码追踪)

在进行源码追踪先,我们先下载tomcat-8.5.72-src源码和tomcat-8.5.72,下载地址:Apache Tomcat? - Apache Tomcat 8 Software Downloads


使用IDEA打开tomcat源码工程

因为tomcat 不是用maven构建的,将tomcat源码工程新建pom.xml文件转为maven项目,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>org.apache.tomcat</groupId>
  <artifactId>apache-tomcat-8.5.50-src</artifactId>
  <name>Tomcat8.5</name>
  <version>8.5</version>
  <build>
    <!--指定源?录-->
    <finalName>Tomcat8.5</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.1</version>
        <configuration>
          <encoding>UTF-8</encoding>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <!--tomcat 依赖的基础包-->
  <dependencies>
    <dependency>
      <groupId>org.easymock</groupId>
      <artifactId>easymock</artifactId>
      <version>3.4</version>
    </dependency>
    <dependency>
      <groupId>ant</groupId>
      <artifactId>ant</artifactId>
      <version>1.7.0</version>
    </dependency>
    <dependency>
      <groupId>wsdl4j</groupId>
      <artifactId>wsdl4j</artifactId>
      <version>1.6.2</version>
    </dependency>
    <dependency>
      <groupId>javax.xml</groupId>
      <artifactId>jaxrpc</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>javax.xml.soap</groupId>
      <artifactId>javax.xml.soap-api</artifactId>
      <version>1.4.0</version>
    </dependency>

  </dependencies>
</project>

如果你下载的是tomcat10+版本,还需要在pom中添加下面这两个jar包:

<dependency>
   <groupId>org.apache.tomcat</groupId>
   <artifactId>jakartaee-migration</artifactId>
   <version>1.0.0</version>
</dependency>

<dependency>
   <groupId>biz.aQute.bnd</groupId>
   <artifactId>biz.aQute.bndlib</artifactId>
   <version>5.3.0</version>
   <scope>provided</scope>
</dependency>

在pom.xml右键,选择Add as Maven project

在org.apache.catalina.startup.ContextConfig#contextConfig 方法中添加jsp解析,

context.addServletContainerInitializer(new JasperInitializer(), null);

因为tomcat源码工程的webapps里面的工程都是没有经过编译的示例工程,将下载的经过编译的二进制工程中的webapps和conf目录覆盖到tomcat源代码工程中。并在jvm启动参数中添加tomcat启动参数,指定tomcat主目录和配制文件目录。

-Dcatalina.home=D:\projects\apache-tomcat-8.5.50-src\

-Dcatalina.base=D:\projects\apache-tomcat-8.5.50-src\

-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager

-Djava.util.logging.config.file=D:\projects\apache-tomcat-8.5.50-src/conf/logging.properties

Tomcat启动入口在org.apache.catalina.startup.Bootstrap中的main函数中,启动它。启动成功后访问127.0.0.1:8080 ,成功!

Tomcat请求分析:

1、org.apache.tomcat.util.net.NioEndpoint.SocketProcessor#doRun --> 接收Socket请求

org.apache.coyote.http11.Http11Processor#service--> 封装tomcat req 和res

2、org.apache.catalina.connector.CoyoteAdapter#service --> 将tomcat req 和res 转换为ServletRequest 和ServletResponse

3、org.apache.catalina.core.StandardEngineValve#invoke -->根据请求的服务器名称,选择合适的子主机来处理此请求。 如果找不到匹配的主机,则返回相应的 HTTP 错误。

4、org.apache.catalina.core.StandardHostValve#invoke -->根据Content部署的地址查找应用

5、org.apache.catalina.core.StandardContextValve#invoke -->根据指定的请求 URI,选择适当的子 Wrapper 来处理此请求。 如果找不到匹配的 Wrapper,则返回相应的 HTTP 错误。在进行Wrapper 查找之前先判断路径是有WEB-INF等路径,这些路径返回404

6、org.apache.catalina.core.StandardWrapperValve#invoke -->调用正在管理的 servlet,遵守有关 servlet 生命周期和 SingleThreadModel 支持的规则。

7、org.apache.catalina.core.ApplicationFilterChain#doFilter -->调用此链中的下一个过滤器,传递指定的请求和响应。 如果此链中没有更多过滤器,则调用 servlet 本身的service()方法。

3 Tomcat性能调优

我们看一个服务器的性能指标主要是从两个方面着手:

  1. 响应时间,tomcat完成一个操作需要的时间
  2. 吞吐量,单位时间内能够完成的操作的数量,单位为数量/秒,一个操作从发起到服务器响应请求。

我们tomcat性能优化从两个方面入手:

  1. 优化JVM内存模型
  2. Tomcat调整线程池或者IO模型

3.1JVM内存模型及JVM内存参数调优

理解了JVM内存模型,我们才能更好的去对JVM内存调优。

本地方法栈:C++Native执行所需的栈区,为线程私有

程序计数器:保存程序执行的位置,为线程私有

栈:程序运行时方法的临时变量保存区域,为线程私有,保存的也是线程的执行位置

堆:存储对象

元数据区(方法区):静态变量、方法、类加载器保存区域

1)JVM内存参数调优

我们对JVM内存优化主要是对堆内存进行调整,其中JVM内存参数如下表:

参数

说明

配置建议

-server

启动Server,以服务端模式运?

服务端模式建议开启

-Xms

最?堆内存

建议与-Xmx设置相

-Xmx

最?堆内存

建议设置为可?内存的80%,这个可用内存是其他软件使用完毕后还可以剩下使用的内存

-XX:MetaspaceSize

元空间初始值


-
XX:MaxMetaspaceSize

元空间最?内存

默认?限

-XX:NewRatio

年轻代和?年代???值,取值为整数,默
认为2

不需要修改

-XX:SurvivorRatio

Eden区与Survivor区??的?值,取值为整
数,默认为8

不需要修改

参数调整示例:

我的计算机是8G内存,开机后剩余内存5G,我以服务模仿是运行,最大堆内存和最小堆内存相同,占用5G * 80% = 4G,其他参数不需要调整。

JAVA_OPTS="-server -Xms4096m -Xmx4096m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

使用jmap 查看tomcat内存使用情况:

netstat -ano 找到tomcat的pid

输入jmap -heap 8940 ,查看tomcat内存使用情况


  1. JVM垃圾回收机制调参

GC策略

说明

串行收集器

单线程执行所有的垃圾回收工作,适用于单核CPU服务器。工作进程--->同一个线程GC工作-->工作进程

并行收集器

以并行的方式执行年轻代的垃圾回收,可以降低垃圾回收的开销。适用于多核处理器多线程的硬件上的数据量较大的应用。工作进程--->多个线程GC工作--->工作进程

并发收集器

并发的方式执行大部分垃圾回收工作,以缩短回收暂停时间。使用于那些响应时间优先于吞吐量的应用,因为该收集器缩短了暂停时间,但会降低应用程序性能。

CMS收集器

并发标记清除收集器,使用于那些更愿意缩短垃圾回收暂停时间并负担起与垃圾回收共享处理资源的应用。

G1收集器

适用于大容量内存的多核服务器,可以在满足垃圾回收暂停时间目标的同时,以最大可能性实现高吞吐量。该机制在JDK1.7之后才支持。

垃圾回收机制参数配置如下表所示:

参数

描述

-XX:+UseSerialGC

启?串?收集器

-XX:+UseParallelGC

启?并?垃圾收集器,配置了该选项,那么 -XX:+UseParallelOldGC默认启?

-XX:+UseParNewGC

年轻代采?并?收集器,如果设置了 -XX:+UseConcMarkSweepGC选项,?动启?

-XX:ParallelGCThreads

年轻代及?年代垃圾回收使?的线程数。默认值依赖于JVM使?的CPU个数

-XX:+UseConcMarkSweepGC(CMS)

对于?年代,启?CMS垃圾收集器。 当并?收集器?法满?应?的延迟需
求是,推荐使?CMS或G1收集器。启?该选项后, -XX:+UseParNewGC?动启?。

-XX:+UseG1GC

启?G1收集器。 G1是服务器类型的收集器, ?于多核、?内存的机器。它在保持?吞吐量的情况下,?概率满?GC暂停时间的?标。

参数示例:

JAVA_OPTS="-XX:+UseConcMarkSweepGC"

3.2 tomcat配置调优

  • 在配置文件中,调整conf/server.xml,配置tomcat线程池


对性能影响的线程池参数如下表所示:

参数

说明

maxConnections

最?连接数,当到达该值后,服务器接收但不会处理更多的请求, 额外的请求将会阻塞直到连接数低于maxConnections 。可通过ulimit -a 查看服务器限制。对于CPU要求更?(计算密集型)时,建议不要配置过? ; 对于CPU要求不是特别?时,建议配置在2000左右(受服务器性能影响)。 当然这个需要服务器硬件的?持。

maxThreads

最?线程数,需要根据服务器的硬件情况,进??个合理的设置

acceptCount

最?排队等待数,当服务器接收的请求数量到达maxConnections ,此时Tomcat会将后?的请求,存放在任务队列中进?排序, acceptCount指的就是任务队列中排队等待的请求数 。?台Tomcat的最?的请求处理数量,是maxConnections+acceptCount。


  • 配置conf/server.xml,禁用AJP连接器


  • 配置conf/server.xml,调整IO模式

Tomcat8之前默认使用BIO,每一个请求创建一个线程,不适用高并发;Tomcat8以后版本默认使用NIO。

Tomcat并发性能有较?要求或者出现瓶颈时,我们可以尝试使?APR模式, APR(Apache PortableRuntime)是从操作系统级别解决异步IO问题,使?时需要在操作系统上安装APR和Native(因为APR原理是使?使?JNI技术调?操作系统底层的IO接?)

  • 动静分离

可以使用Nginx+tomcat相结合,tomcat比较擅长处理动态资源,可以将静态资源丢给Nginx处理。

相关推荐

手机看视频卡顿的系统化解决方案(手机看视频特别卡是什么原因)

手机看视频卡顿是日常使用中常见的问题,通常由网络不稳定、设备性能不足、应用设置不当或系统问题导致。以下方案按优先级排序,兼顾操作难度与解决效果,帮助你快速定位并解决卡顿问题。一、网络优化:解决最核心诱...

电子电路硬件测试基础-板级测试工具之示波器(1)

示波器介绍示波器,作为一种电子测试仪器,主要用于对电信号的波形进行测量、显示与分析。它的工作原理是将电信号的瞬时值转化为屏幕上的垂直偏移,并以时间为基准在水平轴上展开,进而生成信号的图形化呈现。借助示...

宽带安装避坑指南:从选套餐到验收全流程注意事项

在网络成为生活必需品的当下,看似简单的宽带安装实则暗藏诸多细节,稍不留意就会踩入隐藏费用、性能虚标等陷阱。掌握以下关键要点,助你轻松避开宽带安装雷区。一、选对运营商与套餐,拒绝隐形消费选择运营商时,不...

噪声分析及小信号测试(噪音中的信号是什么含义)

示波器在大量开关电源或大功率用电器等复杂环境下进行自环测试,容易出现视频中的噪声问题。示波器在进行小信号测试时,我们要考虑地线带来的干扰,一般设置要求是:一、带宽限制打开;二、尽可能短的地线,一般使用...

怀疑房间被装了监控?深度解析检测方法与步骤

在现代社会中,隐私保护日益受到重视。当我们租房、住酒店或身处私人住宅时,如果发现房间内有异常的迹象,如电器无故自动开关、网络速度突然变慢、电费异常增加,或者在某些特定区域总感觉“被注视”,这些都可能是...

如何快速鉴定网线好坏?怎样测网线电阻?

一、快速鉴定网线好坏的3大核心技巧1.外观检测法:5秒看穿网线本质标识验证:正品网线外皮应清晰印有"CAT5e""CAT6"等类别标识,以及米数刻度(如30...

网速慢?教你快速检测网卡速率! #网络测速

网卡是百兆还是千兆?你知道吗?网速慢可能就是因为网卡不够快,今天就来教你如何快速检查网卡。1.打开CMD:首先找到电脑,按下Win+R键,输入cmd,然后按回车,这就打开了命令提示符...

怎么查自己家WiFi多少兆?(苹果手机测wifi网速)

怎么查自己家WiFi多少兆?在数字化生活日益普及的今天,家庭WiFi的速度直接影响着我们上网冲浪、在线学习、视频通话等日常活动的流畅度。了解自家WiFi的带宽通常以兆比特每秒Mbps为...

家里宽带网速不理想?测一下就知道了

家里网络不理想,经常发生卡顿或是网络抖动?可能是家里宽带的影响,这时候不妨实际测试下网速看看,是否符合预期。个人最近经历过两次上网卡顿问题,第一个是上个月回老家,刷剧的时候,发现有加载缓慢的情况,10...

Linux 必备的20个核心知识点(linux内核知识点)

学习和使用Linux所必备的20个核心知识点。这些知识点涵盖了从基础操作到系统管理和网络概念,是构建扎实Linux技能的基础。Linux必备的20个知识点1.Linux文件系统层级标...

忆享科技戟星安全实验室|内网隧道技术,你知道几个?

本文约6000字,阅读约需15分钟。0x00前言在实际环境中,会有各种网络设备、防火墙以及入侵检测系统阻止外网与内网的通信,我们构建内网隐蔽通道来突破安全策略的限制,实现对目标机器的控制。当我们在外...

Win10怎么连接linux虚拟机?(win10搭建linux虚拟机)

这个不难,主要是ping一下ip,检查win10系统是否和Linux虚拟机连通,接着开启ssh服务,如果一切正常的话,就可以连接linux虚拟机,这里简单介绍一下实现过程,实验环境win10+vmwa...

如何在CentOS中配置静态IP地址?(centos6.5配置静态ip地址)

静态IP地址是在计算机网络中使用的固定IP地址,与动态分配的IP地址相对。在CentOS中,通过配置网络设置,您可以轻松地为您的系统设置静态IP地址。本文将详细介绍在CentOS中配置静态IP地址的步...

Firewalld的简单使用(firewall direct)

从Centos7开始默认防火墙就已经更换为了firewall,替换了之前的iptablesFirewalld服务采用firewall-cmd或firewall-config(图形化工具)来动态管理ke...

Vmware设置静态IP和主机访问(vmware 设置固定ip)

摘要:Vmware设置静态IP和主机访问关键词:Vmware、静态IP、主机访问整体说明最近在做大模型的本地部署,发现之前的Virtualbox,不太能用到本地的英伟达GPU,所以开始使用...

取消回复欢迎 发表评论: