TOMCAT优化

TOMCAT优化

Tomcat是我们经常使用的 servlet容器之一,甚至很多线上产品都使用 Tomcat充当服务器。而且优化后的Tomcat性能提升显著,本文从以下几方面进行分析优化。

一、内存优化
默认情况下Tomcat的相关内存配置较低,这对于一些大型项目显然是不够用的,这些项目运行就已经耗费了大部分内存空间,何况大规模访问的情况。即使是本文中的这个只有一个页面的超小项目,在并发达到一定程度后就会抛出以下类似异常:
Java代码  收藏代码
严重: Exception invoking periodic operation: java.lang.OutOfMemoryError: Java heap space

严重: Error processing request java.lang.OutOfMemoryError: GC overhead limit exceeded
说明Tomcat已经无力支持访问处理,内部GC也已经“无能无力”。所以一般情况下我们需要重新配置Tomcat的相关内存大小。
1.修改内存等 JVM相关配置
Linux下修改TOMCAT_HOME/bin/catalina.sh,在其中加入,可以放在CLASSPATH=下面:
Java代码  收藏代码
JAVA_OPTS=”-server -XX:PermSize=512M -XX:MaxPermSize=1024m -Xms2048m -Xmx2048m”
windows下修改TOMCAT_HOME/bin/catalina.bat,在其中加入,可以放在set CLASSPATH=下面:
Java代码  收藏代码
set JAVA_OPTS=-server -XX:PermSize=512M -XX:MaxPermSize=1024m -Xms2048m -Xmx2048m
这些参数在我们学习JVM部分文章时已经都认识过了,不过这里还是简单介绍下:
-server:启用 JDK的 server 版本;
-Xms:Java虚拟机初始化时堆的最小内存,一般与 Xmx配置为相同值,这样的好处是GC不必再为扩展内存空间而消耗性能;
-Xmx:Java虚拟机可使用堆的最大内存;
-XX:PermSize:Java虚拟机永久代大小;
-XX:MaxPermSize:Java虚拟机永久代大小最大值;
除了这些参数外您还可以根据具体需要配置其他参数,参数的配置可以参考JVM参数的配置。
2.验证
设置成功后我们可以利用JDK自带的工具进行验证,这些工具都在JAVA_HOME/bin目录下:
1)jps:用来显示本地的java进程,以及进程号,进程启动的路径等。
2)jmap:观察运行中的JVM 物理内存的占用情况,包括Heap size , Perm size 等。
更多参数及用法可以参考:http://286.iteye.com/blog/1922630
进入命令行模式后,进入JAVA_HOME/bin目录下,然后输入jps命令:
Java代码  收藏代码
jps
#显示以下结果
2340 Bootstrap
6696 Jps
其中 Bootstrap进程就是我们启动了的 Tomcat,其进程号为2340.
然后我们利用 jmap工具查看其内存相关配置:
Java代码  收藏代码
jmap -heap 2340
#显示以下结果
Attaching to process ID 2340, please wait…
Debugger attached successfully.
Server compiler detected.
JVM version is 24.65-b04

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize      = 2147483648 (2048.0MB)    //最大堆内存
NewSize          = 1310720 (1.25MB)
MaxNewSize       = 17592186044415 MB
OldSize          = 5439488 (5.1875MB)
NewRatio         = 2
SurvivorRatio    = 8
PermSize         = 536870912 (512.0MB)      //永久代初始大小
MaxPermSize      = 1073741824 (1024.0MB)    //永久代最大大小
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 537919488 (513.0MB)
used     = 247606720 (236.13616943359375MB)
free     = 290312768 (276.86383056640625MB)
46.03044238471613% used
From Space:
capacity = 89128960 (85.0MB)
used     = 0 (0.0MB)
free     = 89128960 (85.0MB)
0.0% used
To Space:
capacity = 89128960 (85.0MB)
used     = 0 (0.0MB)
free     = 89128960 (85.0MB)
0.0% used
PS Old Generation
capacity = 1431830528 (1365.5MB)
used     = 0 (0.0MB)
free     = 1431830528 (1365.5MB)
0.0% used
PS Perm Generation
capacity = 536870912 (512.0MB)
used     = 20770360 (19.80815887451172MB)
free     = 516100552 (492.1918411254883MB)
3.86878103017807% used

12005 interned Strings occupying 1039352 bytes.
从结果就可以看出我们的配置已经成功了。

二、配置优化
我们知道TOMCAT_HOME/conf/server.xml可以配置端口,虚拟路径等等 Tomcat相关主要配置。
1.Connector 优化
Connector是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以 Connector的优化是重要部分。默认情况下 Tomcat只支持200线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力。
修改这部分配置需要修改TOMCAT_HOME/conf/server.xml,打开server.xml找到Connector 标签项,默认配置如下:
Xml代码  收藏代码
<Connector port=”8080″ protocol=”HTTP/1.1″
connectionTimeout=”20000″
redirectPort=”8443″ />
其中port代表服务接口;protocol代表协议类型;connectionTimeout代表连接超时时间,单位为毫秒;redirectPort代表安全通信(https)转发端口,一般配置成443。
可以看到除了这几个基本配置外并无特殊功能,所以我们需要对 Connector 进行扩展。
其中Connector 支持参数属性可以参考Tomcat官方网站(https://tomcat.apache.org/tomcat-8.0-doc/config/http.html),非常多,所以本文就只介绍些常用的。
我们将 Connector 配置修改为如下:
Java代码  收藏代码
<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″ />

1)port:代表Tomcat监听端口,也就是网站的访问端口,默认为8080,可以根据需要改成其他。
2)protocol:协议类型,可选类型有四种,分别为BIO(阻塞型IO),NIO,NIO2和APR。
(1)BIO:BIO(Blocking I/O),顾名思义,即阻塞式I/O操作,表示Tomcat使用的是传统的Java I/O操作(即java.io包及其子包)。Tomcat在默认情况下,是以bio模式运行的。遗憾的是,就一般而言,bio模式是三种运行模式中性能最低的一种。BIO配置采用默认即可。
(2)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)更好的并发运行性能。要让Tomcat以nio模式来运行也比较简单,我们只需要protocol类型修改为:
Java代码  收藏代码
//NIO
protocol=”org.apache.coyote.http11.Http11NioProtocol”
//NIO2
protocol=”org.apache.coyote.http11.Http11Nio2Protocol”
即可。
(3)APR:APR(Apache Portable Runtime/Apache可移植运行时),是Apache HTTP服务器的支持库。你可以简单地理解为:Tomcat将以JNI的形式调用 Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地提高 Tomcat对静态文件的处理性能。
与配置 NIO运行模式一样,也需要将对应的 Connector节点的 protocol属性值改为:
Java代码  收藏代码
protocol=”org.apache.coyote.http11.Http11AprProtocol”
相关APR介绍及配置会在下面专门讲。
3)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即可。
4)minSpareThreads:线程的最小运行数目,这些始终保持运行。如果未指定,默认值为10。
5)acceptCount:当所有可能的请求处理线程都在使用时传入连接请求的最大队列长度。如果未指定,默认值为100。一般是设置的跟 maxThreads一样或一半,此值设置的过大会导致排队的请求超时而未被处理。所以这个值应该是主要根据应用的访问峰值与平均值来权衡配置。
6)maxConnections:在任何给定的时间内,服务器将接受和处理的最大连接数。当这个数字已经达到时,服务器将接受但不处理,等待进一步连接。NIO与NIO2的默认值为10000,APR默认值为8192。
7)connectionTimeout:当请求已经被接受,但未被处理,也就是等待中的超时时间。单位为毫秒,默认值为60000。通常情况下设置为30000。
8)maxHttpHeaderSize:请求和响应的HTTP头的最大大小,以字节为单位指定。如果没有指定,这个属性被设置为8192(8 KB)。
9)tcpNoDelay:如果为true,服务器socket会设置TCP_NO_DELAY选项,在大多数情况下可以提高性能。缺省情况下设为true。
10)compression:是否启用gzip压缩,默认为关闭状态。这个参数的