Tomcat优化

Posted sunjzz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat优化相关的知识,希望对你有一定的参考价值。

Tomcat优化

内存优化

Linux下修改TOMCAT_HOME/bin/catalina.sh,在其中加入,可以放在CLASSPATH=下面:

JAVA_OPTS="-server -Xms128m -Xmx128m"  

-server:启用 JDK的 server 版本;

-Xms:Java虚拟机初始化时堆的最小内存,一般与 Xmx配置为相同值,这样的好处是GC不必再为扩展内存空间而消耗性能;

-Xmx:Java虚拟机可使用堆的最大内存;

设置成功后我们可以利用JDK自带的工具进行验证,这些工具都在JAVA_HOME/bin目录下:

  1. jps:用来显示本地的java进程,以及进程号,进程启动的路径等。
  2. jmap:观察运行中的JVM 物理内存的占用情况,包括Heap size , Perm size 下载地址 等。

    [[email protected] ~]$ jps
    2417 Bootstrap
    28725 Jps
    
    [[email protected] ~]$ jmap -heap 2417
    Attaching to process ID 2417, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 25.131-b11
    
    using thread-local object allocation.
    Mark Sweep Compact GC
    
    Heap Configuration:
       MinHeapFreeRatio = 40
       MaxHeapFreeRatio = 70
       MaxHeapSize  = 134217728 (128.0MB)  #最大堆内存
       NewSize  = 44695552 (42.625MB)
       MaxNewSize   = 44695552 (42.625MB)
       OldSize  = 89522176 (85.375MB)
       NewRatio = 2
       SurvivorRatio= 8
       MetaspaceSize= 21807104 (20.796875MB)
       CompressedClassSpaceSize = 1073741824 (1024.0MB)
       MaxMetaspaceSize = 17592186044415 MB
       G1HeapRegionSize = 0 (0.0MB)
    
    Heap Usage:
    New Generation (Eden + 1 Survivor Space):
       capacity = 40239104 (38.375MB)
       used = 705264 (0.6725921630859375MB)
       free = 39533840 (37.70240783691406MB)
       1.7526831611359934% used
    Eden Space:
       capacity = 35782656 (34.125MB)
       used = 672856 (0.6416854858398438MB)
       free = 35109800 (33.483314514160156MB)
       1.8803970281021063% used
    From Space:
       capacity = 4456448 (4.25MB)
       used = 32408 (0.03090667724609375MB)
       free = 4424040 (4.219093322753906MB)
       0.7272159352022058% used
    To Space:
       capacity = 4456448 (4.25MB)
       used = 0 (0.0MB)
       free = 4456448 (4.25MB)
       0.0% used
    tenured generation:
       capacity = 89522176 (85.375MB)
       used = 13473104 (12.848953247070312MB)
       free = 76049072 (72.52604675292969MB)
       15.050018444591874% used
    
    12168 interned Strings occupying 1708120 bytes.

配置优化

Connector

Connector是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以 Connector的优化是重要部分。默认情况下 Tomcat只支持200线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力。修改这部分配置需要修改TOMCAT_HOME/conf/server.xml,打开server.xml找到Connector 标签项,默认配置如下:

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

port代表服务接口;protocol代表协议类型;connectionTimeout代表连接超时时间,单位为毫秒;redirectPort代表安全通信(https)转发端口,一般配置成8443。
可以看到除了这几个基本配置外并无特殊功能,所以我们需要对 Connector 进行扩展。Connector 支持参数属性可以参考Tomcat官方网站(https://tomcat.apache.org/tomcat-8.0-doc/config/http.html),所以本文就只介绍些常用的。

我们将 Connector 配置修改为如下:

<Connector port="8080"   
      protocol="HTTP/1.1"   
      maxThreads="1000"   
      minSpareThreads="100"   
      acceptCount="1000"  
      maxConnections="1000"  
      connectionTimeout="20000"   
      maxHttpHeaderSize="8192"  
      tcpNoDelay="true"  
      compression="on"  
      compressionMinSize="2048"  
      disableUploadTimeout="true"  
      redirectPort="8443"  
      enableLookups="false"  
      URIEncoding="UTF-8" />  

port:代表Tomcat监听端口,也就是网站的访问端口,默认为8080,可以根据需要改成其他。
protocol:协议类型,可选类型有四种,分别为BIO(阻塞型IO),NIO,NIO2和APR。

BIO:BIO(Blocking I/O),顾名思义,即阻塞式I/O操作,表示Tomcat使用的是传统的Java I/O操作(即java.io包及其子包)。Tomcat在默认情况下,是以bio模式运行的。遗憾的是,就一般而言,bio模式是三种运行模式中性能最低的一种。BIO配置采用默认即可。

NIO:NIO(New I/O),是Java SE 1.4及后续版本提供的一种新的I/O操作方式(即java.nio包及其子包)。Java nio是一个基于缓冲区、并能提供非阻塞I/O操作的java API,因此nio也被看成是non-blocking I/O的缩写。它拥有比传统I/O操作(bio)更好的并发运行性能。

APR:APR(Apache Portable Runtime/Apache可移植运行时),是Apache HTTP服务器的支持库。你可以简单地理解为:Tomcat将以JNI的形式调用 Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地提高 Tomcat对静态文件的处理性能。

maxThreads:由该连接器创建的处理请求线程的最大数目,也就是可以处理的同时请求的最大数目。如果未配置默认值为200。如果一个执行器与此连接器关联,则忽略此属性,因为该属性将被忽略,所以该连接器将使用执行器而不是一个内部线程池来执行任务。

maxThreads是一个重要的配置属性,maxThreads配置的合理直接影响了Tomcat的相关性能,所以这里我们重点讨论下。maxThreads并不是配置的越大越好,事实上你即使配置成999999也是没有用的,因为这个最大值是受操作系统及相关硬件所制约的,并且最大值并不一定是最优值,所以我们追寻的应该是最优值而不是最大值。

QPS(Query Per Second):每秒查询率QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。我们常常使用 QPS值来衡量一个服务器的性能。

QPS = 并发数 / 平均响应时间
或者
并发数 = QPS * 平均响应时间

一个系统吞吐量通常由QPS、并发数两个因素决定,每套系统的这两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统的吞吐量就上不去了,如果压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换、内存等等其它消耗导致系统性能下降。所谓吞吐量这里可以理解为每秒能处理请求的次数。

所以选择一个合理的 maxThreads值,其实并不是那么容易的事。因为过多的线程只会造成,更多的内存开销,更多的CPU开销,但是对提升QPS确毫无帮助;找到最佳线程数后通过简单的设置,可以让web系统更加稳定,得到最高,最稳定的QPS输出。

我们可以通过以下几种方式来获取 maxThreads的最佳值:

1.通过线上系统不断使用和用户的不断增长来进行性能测试,观察QPS,响应时间,这种方式会在爆发式增长时系统崩溃,如双12等。

2.根据公式计算,服务器端最佳线程数量=((线程等待时间+线程cpu时间)/线程cpu时间) * cpu数量,这种方式有时会被误导,因为某些系统处理环节可能会耗时比较长,从而影响公式的结果。

3.单、多用户压力测试,查看CPU的消耗,然后直接乘以百分比,再进行压测,一般这个值的附近应该就是最佳线程数量,这种方式理想场景比较适用,实际情况会比这个复杂的多。

4.根据系统的自身情况调整,如硬件限制,系统限制,程序处理能力限制等。

5.定期修改为不同的 maxThreads值,看服务器响应结果及用户反应。

QPS和线程数的关系

1.在最佳线程数量之前,QPS和线程是互相递增的关系,线程数量到了最佳线程之后,QPS持平,不在上升,甚至略有下降,同时相应时间持续上升。

2.同一个系统而言,支持的线程数越多(最佳线程数越多而不是配置的线程数越多),QPS越高。

QPS和响应时间的关系

1.对于一般的web系统,响应时间一般有CPU执行时间+IO等待时间组成。

2.CPU的执行时间减少,对QPS有实质的提升,IO时间的减少,对QPS提升不明显。如果要想明显提升QPS,优化系统的时候要着重优化CPU消耗大户。

所以想要找出 maxThreads的最优值可并不容易,没有最好只有更好,更好的值只能通过时间来显现,如果你不想考虑那么多,一般情况下设置成1000即可。

minSpareThreads:线程的最小运行数目,这些始终保持运行。如果未指定,默认值为10。

acceptCount:当所有可能的请求处理线程都在使用时传入连接请求的最大队列长度。如果未指定,默认值为100。一般是设置的跟 maxThreads一样或一半,此值设置的过大会导致排队的请求超时而未被处理。所以这个值应该是主要根据应用的访问峰值与平均值来权衡配置。

maxConnections:在任何给定的时间内,服务器将接受和处理的最大连接数。当这个数字已经达到时,服务器将接受但不处理,等待进一步连接。NIO与NIO2的默认值为10000,APR默认值为8192。

connectionTimeout:当请求已经被接受,但未被处理,也就是等待中的超时时间。单位为毫秒,默认值为60000。通常情况下设置为30000。

maxHttpHeaderSize:请求和响应的HTTP头的最大大小,以字节为单位指定。如果没有指定,这个属性被设置为8192(8 KB)。

tcpNoDelay:如果为true,服务器socket会设置TCP_NO_DELAY选项,在大多数情况下可以提高性能。缺省情况下设为true。

compression:是否启用gzip压缩,默认为关闭状态。这个参数的可接受值为“off”(不使用压缩),“on”(压缩文本数据),“force”(在所有的情况下强制压缩)。

compressionMinSize:如果compression="on",则启用此项。被压缩前数据的最小值,也就是超过这个值后才被压缩。如果没有指定,这个属性默认为“2048”(2K),单位为byte。

disableUploadTimeout:这个标志允许servlet Container在一个servlet执行的时候,使用一个不同的,更长的连接超时。最终的结果是给servlet更长的时间以便完成其执行,或者在数据上载的时候更长的超时时间。如果没有指定,设为false。

enableLookups:关闭DNS反向查询。

URIEncoding:URL编码字符集。

Connector 还有很多其他参数,可以参考Tomcat官网,这里只介绍与性能相关的部分。

BIO、NIO、APR

配置 protocol的类型可以使用不同的 Connector处理请求

//BIO  
protocol="HTTP/1.1"  
//NIO  
protocol="org.apache.coyote.http11.Http11NioProtocol"  
//NIO2  
protocol="org.apache.coyote.http11.Http11Nio2Protocol"  
//APR  
protocol="org.apache.coyote.http11.Http11AprProtocol"

并不是说 BIO的性能就一定不如 NIO,这几种类型 Connector之间并没有明显的性能区别,它们之间实现流程和原理不同,所以它们的选择是需要根据应用的类型来决定的。

BIO更适合处理简单流程,如程序处理较快可以立即返回结果。简单项目及应用可以采用BIO。

NIO更适合后台需要耗时完成请求的操作,如程序接到了请求后需要比较耗时的处理这已请求,所以无法立即返回结果,这样如果采用BIO就会占用一个连接,而使用NIO后就可以将此连接转让给其他请求,直至程序处理完成返回为止。

APR可以大大提升Tomcat对静态文件的处理性能,同时如果你使用了HTTPS方式传输的话,也可以提升SSL的处理性能。

线程池

Executor代表了一个线程池,可以在Tomcat组件之间共享。使用线程池的好处在于减少了创建销毁线程的相关消耗,而且可以提高线程的使用效率。要想使用线程池,首先需要在 Service标签中配置 Executor,如下:

<Service name="Catalina">  

    <Executor name="tomcatThreadPool"   
         namePrefix="catalina-exec-"   
         maxThreads="1000"   
         minSpareThreads="100"  
         maxIdleTime="60000"  
         maxQueueSize="Integer.MAX_VALUE"  
         prestartminSpareThreads="false"  
         threadPriority="5"
         className="org.apache.catalina.core.StandardThreadExecutor"/>  
         ....  

name:线程池名称,用于 Connector中指定。

namePrefix:所创建的每个线程的名称前缀,一个单独的线程名称为 namePrefix+threadNumber。

maxThreads:池中最大线程数。

minSpareThreads:活跃线程数,也就是核心池线程数,这些线程不会被销毁,会一直存在。

maxIdleTime:线程空闲时间,超过该时间后,空闲线程会被销毁,默认值为6000(1分钟),单位毫秒。

maxQueueSize:在被执行前最大线程排队数目,默认为Int的最大值,也就是广义的无限。除非特殊情况,这个值不需要更改,否则会有请求不会被处理的情况发生。

prestartminSpareThreads:启动线程池时是否启动 minSpareThreads部分线程。默认值为false,即不启动。

threadPriority:线程池中线程优先级,默认值为5,值从1到10。

className:线程池实现类,未指定情况下,默认实现类为org.apache.catalina.core.StandardThreadExecutor。如果想使用自定义线程池首先需要实现 org.apache.catalina.Executor接口。

线程池配置完成后需要在 Connector中指定:

<Connector executor="tomcatThreadPool"  
...

Listener

另一个影响Tomcat 性能的因素是内存泄露。Server标签中可以配置多个Listener,其中 JreMemoryLeakPreventionListener是用来预防JRE内存泄漏。此Listener只需在Server标签中配置即可,默认情况下无需配置,已经添加在 Server中。

<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />

以上是关于Tomcat优化的主要内容,如果未能解决你的问题,请参考以下文章

使用 C++ 反转句子中的每个单词需要对我的代码片段进行代码优化

如何优化C ++代码的以下片段 - 卷中的零交叉

从JVM的角度看JAVA代码--代码优化

Tomcat8优化--代码优化

Tomcat部署及优化

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段