当前位置: 首页 > 科技观察

架构师之路上的Tomcat性能调优

时间:2023-03-20 19:40:19 科技观察

一、总结前一天的学习从“Day3”的性能测试部分,我们了解到了几个决定性能测试的重要指标。分别是:ThroughputResponsetimeCpuloadMemoryUsage我们在研究过程中对Apache进行了优化,优化了以上四个核心指标的读数。那么我们的Apache调优好了,我们的Tomcat也相应的做了调整。课程结束后,到时候你的“小猫”就真的会“飞”起来了,请仔细阅读。这篇文章一方面是用来给曾经写过《Tomcat如何承受1000个用户》的人解释的,对作者的尊重,一方面是对原文章的扩展,因为申请后就解决了将原文章的知识应用于两个相关的重大项目:承载更大的并发用户数并取得良好的性能提升(系统平均性能提升20倍,极限事务可达80倍)。另外值得一提的是,当时我们在项目中使用的“小猫”是在32位机器上运行的,也就是我们的JVM受限于最大内存2GB,已经跑的“飞了”。.....如果你在64位机器下运行这只“小猫”。.....正如你所想象的,你会得到什么样的效果?请在下方详细设置!2.所有基于JVM的优化(内存)2.1JVM在32位操作系统和64位操作系统的对比我们一般的开发者基本都是使用32位的Windows系统,这就导致了一个比较严重的问题:32-的内存限制位Windows系统。先来看一张对照表:解决完以上问题,我们再相见。有一个新问题,JVM在32位系统下的内存限制:不能超过2GB内存,即使你的机器在Win2003AdvancedServer下配备8GB-16GB内存,而你的JAVA也只能使用2GB记忆。其实我一直想推荐大家使用Linux或者Mac操作系统,并且安装64位的,因为毕竟我们是用来开发的,不是用来玩游戏的,而且Java来自Unix,属于Unix(Linux仅在PC上运行。仅限Unix)。因此,很多开发者运行在win32位系统上,甚至在生产环境中部署win32位系统。那么这个时候如果要优化你的Tomcat,就必须要注意一些技巧。在64位操作系统上,系统内存和JVM都不受2GB这样的限制。Tomcat的优化分为两部分:Tomcat启动命令行中的优化参数为JVM优化。Tomcat容器本身参数的优化(这个和ApacheHttpServer很像)本节先讲Tomcat启动命令行中的参数优化。Tomcat首先运行在JVM上,因为它的启动实际上只是一个java命令行。首先,我们需要调优JAVA的启动命令行。需要注意的是,这里讨论的JVM优化是基于OracleSun的jdk1.6或以上版本,其他JDK或更低版本的JDK不适用。2.2Tomcat启动参数优化Tomcat的启动参数位于tomcat安装目录的in目录下。如果您使用的是Linux操作系统,则为catalina.sh文件。如果您使用的是Windows操作系统,您需要修改的是catalina.bat文件。打开文件,一般文件头是一堆##包裹的注释文字,找到最后一段注释文字如:#$Id:catalina.sh5227972007-03-2707:10:29Zfhanik$#-----------------------------------------------------------------------#OSspecificsupport.$var_must_besettoeithertrueorfalse。输入回车,在linux系统exportJAVA_OPTS="-server-Xms1400M-Xmx1400M-Xss512k-XX:+AggressiveOpts-XX:+UseBiasedLocking-XX:PermSize=128M-XX:MaxPermSize=256M-XX:+AggressiveOpts-XX:+UseBiasedLocking-XX:PermSize=128M-XX:MaxPermSize="-server-Xms1400M-Xmx1400M-Xss512k-XX="-server-Xms1400M-Xmx1400M-Xss512k-XX::+DisableExplicitGC-XX:MaxTenuringThreshold=31-XX:+UseConcMarkSweepGC-XX:+UseParNewGC-XX:+CMSParallelRemarkEnabled-XX:+UseCMSCompactAtFullCollection-XX:LargePageSizeInBytes=128m-XX:+UseFastAccessorMethods-XX:+UseCMSInitiating.DjaawlyOnlyheadless=true"tomcat启动参数setJAVA_OPTS1=-server0-XmsinWindows系统Xmx1400M-Xss512k-XX:+AggressiveOpts-XX:+UseBiasedLocking-XX:PermSize=128M-XX:MaxPermSize=256M-XX:+DisableExplicitGC-XX:MaxTenuringThreshold=31-XX:+UseConcMarkSweepGC-XX:+UseParNewGC-XX:+CMSParallelRemarkEnabled-XX:+UseCMSCompactAtFullCollection-XX:LargePageSizeInBytes=128m-XX:+UseFastAccessorMethods-XX:+UseCMSInitiatingOccupancyOnly-Djava.awt.headless=true上面有这么多参数,可能有些人没见过在一个tomcat启动命令中加入这么多参数,当然这些参数是只是我机器上的可能不适合你,尤其是参数后面的值(value)是一个需要根据你自己的实际情况设置的参数解释:-server我不管你是什么原因,只要你的tomcat是运行在生产环境的其中,这个参数是必须要加上的,因为tomcat默认运行在一个叫java-client的模式,server是指你的tomcat运行在真正的生产模式,也就是当你的tomcat运行在服务器模式下,它将拥有:更大更高的并发处理能力,更快更强大的JVM垃圾回收机制,更大的负载和吞吐量。..甚至。..和更多。..记住Y,不然这个-server加不上去,会被打屁股的。-Xms–Xmx是JVM内存设置。Xms和Xmx设置相同是最好的方法。有人说Xms是最小值,Xmx是最大值,不太好听。这样的设置更加人性化和科学化。改变。人性?科学?你的尺码。我们来思考这样一个场景:随着并发数的增加,系统的内存占用逐渐增加。当它到达最高点时,就不能再上升了,然后开始下降。不要认为这个秋天是好事,因为它是一个大起大落,当内存回落时,它付出的代价就是CPU开始高速运行进行垃圾回收。这时候,甚至会导致你的系统“卡死”。十几秒,因为JVM在垃圾回收。所以,我们一开始就把这两个设置成一样的,这样Tomcat就可以充分利用系统的效率,最大化启动时的参数。这个原因和jdbcconnectionpool中的minpoolsize和maxpoolsize是一样的。原则。我怎么知道我的JVM可以使用最大值?摸头?决不!在设置最大内存,也就是Xmx值的时候,请先打开一个命令行,输入如下命令:看,可以正常显示JDK的版本信息,说明这个可以用了。不是说32位系统最多可以使用2GB内存吗?即:2048m,我们可以试试吗?决不!不说2048m,试试1700m,1700m怎么样?2048m就更不用说了,2048m只是一个理论值,这么说吧,我这里有好几台机器,有的机器-Xmx1800没问题,有的机器只能到-Xmx1500m。所以,在设置-Xms和-Xmx值的时候,一定要记得先这样测试,不然你直接在tomcat启动命令行里加,你的tomcat就再也启动不起来了,不飞了想飞。这只是瘟疫猫。-Xmn将年轻代大小设置为512m。整个堆大小=新生代大小+老年代大小+永久代大小。永久代一般固定大小为64m,所以增加新生代会减小老年代的大小。这个值对系统性能影响很大,Sun官方推荐配置为整个堆的3/8。-Xss指的是设置每个线程的堆栈大小。这取决于你的程序,一个线程需要占用多少内存,可能有多少个线程同时运行等等。一般不容易设置超过1M,否则容易出现内存不足。-XX:+AggressiveOpts顾名思义(aggressive),启用这个参数,每当JDK版本升级时,你的JVM都会使用新添加的优化技术(如果有的话)-XX:+UseBiasedLocking启用优化的线程锁,我们知道那在我们的appserver中,每个http请求都是一个线程,有的请求短有的长,会出现请求排队的现象,甚至线程阻塞,这个优化过的线程锁让你在appserver中自动优化分配线程处理。-XX:PermSize=128M-XX:MaxPermSize=256MJVM使用-XX:PermSize设置非堆内存的初始值,默认为物理内存的1/64;导出数据量大的文件时,一定要设置好这两个Value设置,否则会出现内存溢出错误。最大非堆内存大小由XX:MaxPermSize设置,默认为物理内存的1/4。那么,如果物理内存是4GB,那么64的四分之一就是64MB,这是PermSize的默认值,也就是永久代内存的初始大小;四分之一是1024MB,这是MaxPermSize的默认大小。-XX:+DisableExplicitGC不允许在程序代码中显式调用“System.gc()”。我见过两个在DAO操作结束时手动调用System.gc()的优秀项目。我觉得这样做似乎可以解决他们的内存不足问题。付出的代价是系统响应时间严重降低,就像我在Xms和Xmx中讲解的原理一样。这样调用GC会导致系统的JVM波动很大,性能也上不去!-XX:+UseConcMarkSweepGC是CMSgc。该功能仅在jdk1.5及以后的版本中有。它使用了gc估计触发器和堆占用触发器。我们知道频繁的GC会因为JVM的起伏而影响系统的效率。因此,使用CMSGC后,当GC数量增加时,每次GC的响应时间可以很短。比如使用CMSGC,通过jprofiler观察,GC被触发的次数非常多,每次GC只需要几毫秒。-XX:MaxTenuringThreshold设置垃圾最大年龄。如果设置为0,新生代对象将直接进入老年代,不经过Survivor区。对于老年代较多的应用,可以提高效率。如果该值设置较大,新生代对象会在Survivor区被多次复制,可以增加对象在新生代的存活时间,增加在新生代被回收的概率。该值的设置是本地jprofiler监控后得到的一个理想值,不能一概而论,照搬原样。-XX:+CMSParallelRemarkEnabled使用UseParNewGC时,最小化标记时间-XX:+UseCMSCompactAtFullCollection使用并发gc时,防止内存碎片,组织存活对象,减少内存碎片。-XX:LargePageSizeInBytes指定Java堆的分页页大小-XX:+UseFastAccessorMethodsget,set方法转换为本地代码-XX:+UseCMSInitiatingOccupancyOnly表示只有老年代使用初始化比例时,并发收集器才开始收集-XX:CMSInitiatingOccupancyFraction=70CMSInitiatingOccupancyOccion,设置这个参数有很多技巧,基本满足(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn就不会导致提升失败。在我的应用中,Xmx为6000,Xmn为512,那么Xmx-Xmn为5488兆,即老年代有5488兆,CMSInitiatingOccupancyFraction=90表示老年代90%满时开始执行并发垃圾回收(CMS),此时剩余的10%空间为5488*10%=548兆,所以即使把Xmn中的所有对象(也就是年轻代的512兆)全部移动到老年代generation,548兆字节空间足够,所以只要满足上面的公式,就不会出现垃圾回收期间提升失败的情况;因此,该参数的设置必须与Xmn相关联。-Djava.awt.headless=true这个参数一般用在最后。这个完整参数的作用如下。有时我们在J2EE项目中会用到一些图表工具如:jfreechartforweb网页输出GIF/JPG等流。在winodws环境下,一般我们的app服务器在输出图形的时候不会遇到什么问题,但是在linux/unix环境下,我们经常会遇到异常导致你在winodws开发环境下画图。显示效果不错,但是在linux/unix下无法显示,所以加这个参数可以避免这种情况。上述配置基本可以实现:更快的系统响应时间,更快的JVM回收速度而不影响系统响应速度,最大化JVM内存,最小化线程阻塞,2.3Tomcat容器中的优化。该命令经过优化,增加了系统可用的JVM数量、垃圾收集效率和线程阻塞,提高了系统响应效率。还有一个很重要的指标我们没有优化,就是吞吐量。记得我们第三天学习的时候说过,系统本身可以处理1000个,但是你没有优化配置让它默认只能处理25个。那么我们来看看Tomcat容器中的优化。打开tomcat安装目录下的confserver.xml文件,找到这一行:这么大。没关系,下面就URIEncoding="UTF-8"一一解释,让tomcat解析中文名称文件的url。真的很方便,不像apache有mod_encoding,手动编译maxSpareThreads。maxSpareThreads的意思是,如果你空闲的线程数超过了设定的数量,这些线程就会被挂起,以减少这个池中的线程总数。minSpareThreads最小备用线程数,tomcat启动时初始化的线程数。enableLookups的作用和Apache中的HostnameLookups一样,设置为off。connectionTimeoutconnectionTimeout是以毫秒为单位的网络连接超时。maxThreadsmaxThreadsTomcat使用线程来处理它收到的每个请求。该值表示Tomcat最多可以创建的线程数,即最大并发数。acceptCountacceptCount是当线程数达到maxThreads时,后续的请求会被放入等待队列。这个acceptCount是这个队列的大小。如果队列也满了,直接拒绝连接maxProcessors和minProcessors。Java中的线程就是程序运行的时候。路径是程序中与其他控制线程无关,可以独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员编写高效的程序,最大限度地提高CPU利用率,将空闲时间保持在最低限度,并接受更多请求。通常Windows是1000左右,Linux是2000左右。useURIValidationHack我们看一下tomcat中的一段源码:securityif(connector.getUseURIValidationHack()){Stringuri=validate(request.getRequestURI());if(uri==null){res.setStatus(400);res.setMessage("InvalidURI");thrownewIOException("InvalidURI");}else{req.requestURI().setString(uri);//重做URI解码req.decodedURI().duplicate(req.requestURI());req.getURLDecoder().convert(req.decodedURI(),true);}}可以看到,如果将useURIValidationHack设置为“false”,可以减少对一些url的不必要的检查,减少开销。enableLookups=”false”为了消除DNS查询对性能的影响,我们可以通过修改server.xml文件中enableLookups参数的值来关闭DNS查询。disableUploadTimeout类似于Apache中的keeyalive,为Tomcat配置gzip压缩(HTTP压缩)功能compression="on"compressionMinSize="2048"compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain“HTTP压缩可以大大提高浏览网站的速度。其原理是客户端请求网页后,从服务器端压缩网页文件,然后下载到客户端,由客户端的浏览器负责解压浏览。与HTML、CSS、Javascript、Text的正常浏览过程相比,可节省约40%的流量。更重要的是,它还可以压缩动态生成的网页,包括CGI、PHP、JSP、ASP、Servlet、SHTML等,压缩效率惊人。1)compression=”on”开启压缩功能2)compressionMinSize=”2048″开启压缩输出内容大小,默认为2KB3)noCompressionUserAgents=”gozilla,traviata”以下浏览器不开启压缩4)compressableMimeType="text/html,text/xml" compressiontype最后不要忘记在8443端口添加同样的配置,因为如果我们使用https协议,就会使用8443端口的配置,对?举个真实的例子:之前的项目,经过4轮性能测试,第一轮是完成了问题的定位,第二轮优化apache+tomcat/weblogic,第三轮做集群优化,第四轮优化sql和代码。添加了所有Tomcat优化。结合第三天Apache的性能优化,我们的架构可以“飞”起来了。当然这里也有提到数据库优化的任何步骤,但是仅仅通过这两个步骤,我们的系统就已经大有作为了。很大的推动力。到第二轮时,我们的成绩提高了多少倍?我们先来看一张loaderrunner的截图:左边第一列是第一轮没有做任何调优的压测报告。右边一栏是Apache优化和Tomcat优化后得到的压力测试报告。让我们看看,这改进了多少次?这只是一个改进,没有更改代码。现在明白调好apache和tomcat有多重要了吧?如果加入如下代码,SQL调优,数据库调优。.....所以在我上一个项目中,出现了单笔交易性能(无论是吞吐量还是响应时间)提升80倍的极端例子。