Tomcat6 Optimize
实验条件
数据库服务器
操作系统信息
SYSTEM : Linux flume5 2.6.18-164.el5 #1 SMP Thu Sep 3 03:28:30 EDT 2009 x86_64 GNU/Linux
硬件配置信息
CPU : 4CPU Intel(R) Xeon(R) CPU X5650 @ 2.67GHz Cache size : 12288 KB
MEMORY : 4G
数据库服务器信息
MySql版本:Server version: 5.5.24-log MySQL Community Server (GPL)
备注:修改默认MySql服务器连接限制100为1024
应用服务器
操作系统信息
SYSTEM : Linux flume5 2.6.18-164.el5 #1 SMP Thu Sep 3 03:28:30 EDT 2009 x86_64 GNU/Linux
硬件配置信息
CPU : 4CPU Intel(R) Xeon(R) CPU X5650 @ 2.67GHz Cache size : 12288 KB
MEMORY : 4G
中间件服务器信息
Tomcat版本:apache-tomcat-6.0.32
备注:系统线程数目没有限制(NPTL内核)
以上数据因为服务器公用原因,并不纯净,但基本能保证内存空闲在2.5G上,CPU使用也足够空闲
1 内存
1.1 概念
Tomcat内存优化其实也就是对JVM内存进行设置
1.2 配置位置
配置位置:%TOMCAT_HOME%/bin下,
Window:catalina.bat
Linux/Unix:catalina.sh
首行添加:JAVA_OPTS=-Xms256m –Xmx256m …
注意:Linux环境下:JAVA_OPTS=”-Xms256m –Xmx256m …”,即参数要加引号,不然不起作用
1.3 内存参数配置
以下值大部分情况为默认值,应根据具体服务器硬件配置及业务特性来配置
-Xverify:none关闭大部分类验证措施,以缩短虚拟机类加载时间
-Xms16m 堆内存初始值 (M>1G ? 1G/64 : M/64)
-Xmx256m 堆内存最大值 (M>1G ? 1G/4 : M/4)
-Xmn4m 新生代初始值 (迸发收集器下通过该值来设置,该设置优先级高于-XX:NewRatio)
-XX:NewRatio=2 老年代与新生代比值(迸发收集器下默认=7)
-XX:SurvivorRatio=8 Eden区与Survivro区的比值
-XX:PermSize=12m 永久代初始值
-XX:MaxPermSize=64m 永久代最大值
-Xss1024k 栈内存大小
-XX:MaxTenuringThreshold=15 由年轻代到年老带的GC阀值(迸发收集器下默认=4)
-XX:+UseFastAccessorMethods get,set 方法转成本地代码
1.4 垃圾收集器参数配置
响应时间优先(根据CPU数目及应用具体设置,一般需要CPU>4)
-XX:+UseParNewGC
设置新生代多线程串行收集器,可以不设置
-XX:+UseConcMarkSweepGC
设置老年代迸发收集器,
注意:设置了该值,
①-XX:+UseParNewGC可以不用再设置
②-XX:NewRatio设置失效,因为在迸发收集器下,该值被设置为7即:XX:NewRatio=7, 该种情况下,新生代将足够小(与通常相比),便于进行更多次的新生代垃圾回收(Minor GC,采用的复制算法),设置1.5G内存配置时,新生代才192M,这时进行Minor GC平均才30-50ms,是很小的
建议保留默认值,如果根据业务,需要设置新生代大小,可以用参数:-Xmn
③-XX:MaxTenuringThreshold=4 这个值也比较合理,建议保留,测试修改为2、3时,老年代会比较快就占用内存很多,一旦发生FULL GC,讲非常耗时
-XX:ParallelGCThreads=8
设置并行收集的线程数
-XX:+UseCMSCompactAtFullCollection
打开对年老代的压缩,可能会影响性能,但可以消除碎片
-XX:CMSFullGCsBeforeCompaction=10
由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次Full GC以后对内存空间进行压缩、整理
-XX:CMSInitiatingOccupancyFraction=75
指示在老年代在使用了 n% 的比例后 , 启动 concurrent collector, 默认值是 68
-XX:+UseCMSInitiatingOccupancyOnly
指示只有在老年代在使用了初始化的比例后 concurrent collector 启动收集
吞吐率优先(根据CPU数目及应用具体设置,一般需要CPU>4)
-XX:+UseParallelGC
设置新生代并行收集器
-XX:+UseParallelOldGC
设置老年代并行收集器
-XX:ParallelGCThreads=8
设置并行收集的线程数
-XX:MaxGCPauseMillis=10
置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值
2 线程
2.1 概念
进程是资源管理的最小单位,线程是程序执行的最小单位,即CPU最基本的调度单位。Sun JDK线程实现:基本上操作系统支持怎么样的线程模型,Java 虚拟机就怎么映射。
Window和Linux下,都是1对1线程模型,Unix一般是N对N线程模型,(也可选择1对1线程模型)
32位系统下,Windows单进程最多能分配2G内存,Linux单进程最多能分配3G内存。
注意1:
进程的内存分配是通过虚拟内存来进行分配的(不完全是物理内存),所以查看某一个进程要看其虚拟内存,Windows下,在任务管理器中,可以调出“提交大小”查看,Linux下,可以通过Top的VIRT列查看注意2:线程数计算公式
假定
总内存 TOTAL_M = 2048M(这个Windows下是2048,Linux下是3072)
堆内存HEAM_M = 1024M,
永久代内存:PERM_M= 128M
系统保留内存:RETAIN_M = 130M(一般是100M-150M)
栈分配:-Xss1m
则可产生线程数目为:(TOTAL_M - HEAM_M - PERM_M - RETAIN_M)/1m=(2048-1024-128-130)/1=766
以上计算没有考虑Thread对象本身的大小,在没有对Thread对象引入其余大对象属性时,Thread对象本身是非常小的,基本可以忽略对堆内存的影响。但如果Thread对象引入其余大对象属性,则堆内存的影响需要统计进去,比如堆内存只有20M, 每个Thread对象引入4M的大对象,则实际最多产生4-5个线程,因为这个时候需要考虑对象的内存分配情况了
64位系统下,只要有足够的内存,线程创建数基本没有限制( NPTL内核的64位linux基本没
有限制,即Linux内核>2.6 )
2.2 配置位置
配置位置:%TOMCAT_HOME%/conf下,
server.xml文件
2.3 启用线程池
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="2000" minSpareThreads="500"/>
name:线程池名称,必须唯一
namePrefix:线程池创建线程的名称前缀,线程名称一般为namePrefix+ threadNumber
maxThreads:线程池存活的最大线程数,默认值为200
minSpareThreads:线程池初始线程数(保持活跃的最小线程数),默认值为25
一般只需要配置maxThreads和minSpareThreads即可
具体其余参数可参考:%TOMCAT_HOME%\webapps\docs\ config\executor.html
<Connector executor="tomcatThreadPool"
port="80" protocol=" HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
enableLookups="false"
acceptCount="1024" />
acceptCount:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100
enableLookups:是否反查域名,取值为:true或false。 缺省值为false表示使用客户端主机名的DNS解析功能,被ServletRequest.getRemoteHost方法调用。
compression:压缩传输,取值on/off/force,默认值off?
具体其余参数可参考:%TOMCAT_HOME%\webapps\docs\ config\http.html
3 数据库连接池
3.1 概念
据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能
3.2 配置位置
配置位置:%TOMCAT_HOME%/conf下server.xml文件
具体项目的WEB-INF/web.xml文件
3.3 启用数据库连接池
① <Context path="" docBase="" reloadable="false">
<Resource name="jdbc/ads_mysql" auth="Container"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="10000"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/dataBaseName"/>
</Context>
② 在项目web.xml配置
<resource-ref>
<description>MySQL DataSource</description>
<res-ref-name> jdbc/ads_mysql </res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
③ 将mysql-connector-java-5.1.20-bin.jar类似的数据库驱动连接jar包放置到%TOMCAT_HOME%/lib目录下
注:实际该处一般采用外部数据库连接池实现,比如C3P0,DBCP等
注意:MySql服务器默认只允许打开100个连接,要修改此限制,请在MySql 配置文件my.ini 或 my.cnf查找mysqld, 如下,在[mysqld]添加该配置值
[mysqld]
max_connections = 1024
重新启动生效
查看配置值是否生效:
mysqladmin -uroot -p variables|grep max_connections
连接是否保持?
4 其余优化
4.1 启用NIO
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
修改为:
<Connectorport="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
redirectPort="8443" />
4.2 调整文件打开数
单进程文件打开数:默认Windows下打开文件数是2000,Linux系统默认打开文件数是1024。
实际的生产环境中(获取图片等,都属于打开文件),这个值总是显得太小,经常会报“too many open files” 等这样的错误
导致系统死掉,所以我们总是要修改该值。
Linux下ulimit –a 可以查看,默认是1024
open files (-n) 1024
vi /etc/profile 加入ulimit -n 65535重新启动,就把linux改成打开文件最大数为65535
缓存的图片等?
4.3 精简Tomcat6的配置
删除不需要的一些管理和帮助文件,提高Tomcat的安全性?
4.4 在web下不显示目录列表
在web.xml中把listings改成false就行了?
4.5 启用 Tomcat 的 APR 优化
http://tomcat.apache.org/tomcat-6.0-doc/apr.html(适合没有和其余WEB服务器整合的情况)
5 归纳、建议
归纳起来,Tomcat的优化主要涉及:
内存优化:设置相应的JVM内存参数和确定适合应用的垃圾收集器及其参数
线程优化:采用线程池,并配置相应的参数
数据库连接池优化:选择恰当的数据源,设置相应的数据源配置参数
其余优化:启用NIO、调整数据库本身的连接数目限制、调整服务器文件打开数
其余建议:
内存、线程、数据库连接池等必须一起调整,避免其中某一项成为系统瓶颈(往往前端WEB
服务器,如Httpd,Nginx等,后端数据库本身,也需要做一定的配置调整)
服务器尽量采用64位、4CPU、 4G内存及以上硬件
注意:此种情况下,可以采用64位JDK使用大内存,这时,需要控制FULL GC的频率和时间,
尽量做到不需要出现FULL GC或者夜间定时FULL GC,避免FULL GC造成的停顿,或者通过部署
多32位虚拟机来进行逻辑集群,以充分利用服务器硬件资源。
注意根据CPU数目,业务类别来设置垃圾收集器的类别,同时注意控制GC频率
注意内存、线程池、数据库连接池等的配置,不是越大越好
Tomcat内存参数配置建议书写在catalina.bat(catalina.sh)的首行,并且所有参数书写在同一行
Linux下需要给配置参数加引号
实际压测配置
实际压测配置1
内存:
-Xverify:none –Xms1536m –Xmx1536m -XX:PermSize=128m -XX:MaxPermSize=256m -Xss1024k -XX:ParallelGCThreads=10 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseFastAccessorMethods -XX:+DisableExplicitGC
线程:
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="2000" minSpareThreads="300"
数据库连接池:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://x.x.x.x:3306/mydb?useUnicode=true&characterEncoding=UTF-8
username=root
password=1234
#初始化连接:连接池启动时创建的初始化连接数量
initialSize=200
#最大活动连接:连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制
maxActive=-1
#最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制
maxIdle=300
#最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建
minIdle=200
#最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为-1表示无限等待
maxWait=18000
#在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. 如果设置为非正数,则不运行空闲连接回收器线程
timeBetweenEvictionRunsMillis=60000
#在每次空闲连接回收器线程(如果有)运行时检查的连接数量numTestsPerEvictionRun=5
#连接在池中保持空闲而不被空闲连接回收器线程(如果有)回收的最小时间值,单位毫秒
minEvictableIdleTimeMillis=180000
#标记是否删除泄露的连接,如果他们超过了removeAbandonedTimout的限制.如果设置为true, 连接被认为是被泄露并且可以被删除,
#如果空闲时间超过removeAbandonedTimeout. 设置为true可以为写法糟糕的没有关闭连接的程序修复数据库连接
removeAbandoned=true
#泄露的连接可以被删除的超时值, 单位秒
removeAbandonedTimeout=300
#指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个.
#注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串testOnBorrow=true
① 300用户做名字查询和名字+日期组合查询两种情况
实际压测部分图示
A:应用服务器系统负载情况
略
CPU使用基本在20%以下
B:客户端visualvm监控垃圾回收情况
略
从中可见新生代每次垃圾回收时间是很短:4.937/253=20毫秒,老年代短时间消耗内存也不是很多
C:LoadRunner压力测试情况
略
300用户下通过总数408573,失败总数为8(很低了),根据名称查询和根据名称+日期查询平均响应时间和最大响应时间都不算大(登陆和退出不予考虑)
② 500用户做名字查询和名字+日期组合查询两种情况
LoadRunner压力测试情况
略
500用户下通过总数234121,失败总数为30(比较低),根据名称查询和根据名称+日期查询平均响应时间和最大响应时间明显增大(登陆和退出不予考虑)
以上分析可见:500用户下,吞吐量(负载)和响应时间(响应)都有明显的下降
实际压测配置2
-Xverify:none –Xms2048m –Xmx2048m -XX:PermSize=128m -XX:MaxPermSize=256m -Xss1024k -XX:ParallelGCThreads=10 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseFastAccessorMethods -XX:+DisableExplicitGC
① 500用户做名字查询和名字+日期组合查询两种情况【服务器线程池初始300】
实际压测部分图示
A:应用服务器系统负载情况
略
系统CPU使用率基本在20%以下,1分钟、5分钟、15分钟平均负载(0.26、0.27、0.14)也比较正常(该测试仅仅做了几个简单的查询)
B:客户端visualvm监控垃圾回收情况
略
新生代每次垃圾回收时间是很短:2.298/71=32毫秒,老年代短时间消耗内存也不是很多
C:LoadRunner压力测试情况
略
500用户下通过总数411307,失败总数为50,其中按名称查询失败27次,根据名称查询和根据名称+日期查询平均响应时间和最大响应时间都不算大(登陆和退出不予考虑)
整体上在前台直接操作WEB进行登录,查询等,速度也还比较快,没什么特别缓慢的感觉
② 700用户做名字查询和名字+日期组合查询两种情况【服务器线程池初始500】
C:LoadRunner压力测试情况
略
700用户下通过总数604124,失败总数为324,其中按名称查询失败159次,根据名称查询和根据名称+日期查询平均响应时间和最大响应时间依然都不算大(登陆和退出不予考虑)
整体上在前台直接操作WEB进行登录,查询等,速度也还比较快,没什么特别缓慢的感觉
以上测试大致说明在目前测试的服务器上内存1.5G,300迸发情况下是系统比较稳定,当达到500迸发时,系统吞吐量和响应速率明显下降,内存调整到2G时,500迸发下系统也比较稳定
吞吐量和响应速率也比较高,不过有些细小失败(未查明),但应当不影响系统使用,700迸[/size]发下系统吞吐量和响应速率依然不错,但失败增多,该种情况,该配置不是很可取