最近工作的性能优化

开发直播系统一年了,说真的一直没有好好做做性能调优,一方面是目前用户少,机器毫无压力,而功能比较杂优先关注功能,另一方面就是自己懒,不专业,所以只是做了简单的打压测试就完事了。没有服务异常也就没有往这方面做优化。现在有一段时间功能上没有太大的压力,于是开始自己折腾,一边把resin4.0.14升到了tomcat8.10,jdk1.6升到了1.8(无论哪家jdk,都应该用最新的版本),缓存加了redis,从单机升了多机。经过了这么一个版本,还是需要好好做一下性能测试的。以下是我的一些处理:

优化的方向

  1. 吞吐、并发数、平均响应时间、系统资源(内存和cpu)

1. 提取request log,重组打压请求

对于性能测试请求,不能再过account server,就在每一个url后面加上userId,作为parameters,解析出来后加入到attribute里面。 对于主要推送系统服务,还是依赖于外部系统。这个也没得改变。打压时就一起打了吧。

2. 代码上的优化

2.1 httpclient

将httpClient封装一下,不再一次次的建立连接获取别的服务器的http接口数据了。之前用的是HttpClient4.1.2,升级到4.5.1,加入PoolingHttpClientConnectionManager来避免多次连接。调整keepalive的时间。 PoolingClientConnectionManager pccm = new PoolingClientConnectionManager();
pccm.setMaxTotal(NewsConstant.MAXTOTAL);//这里是控制最多的连接的数目,跟连接的host无关
pccm.setDefaultMaxPerRoute(NewsConstant.MAX
ROUTE_TOTAL);//这个可以理解是每一个host的最多连接数目
HttpClients.custom().setConnectionManager(connManager).build();

conMgr.setMaxTotal(200);

2.2 hibernate

在hibernate缓存上再加一层内存的缓存,这样直接就获取到了mysql中的数据了。虽然更新最多会1分钟后有效,但对于压测还是很有帮助的。我们的库主要也是课程信息这一张表,所以用HashMapCache来做这个事就好了。

3. tomcat的调整

String servletHome = servletContextEvent.getServletContext().getRealPath("WEB-INF")调整为: String servletHome = servletContextEvent.getServletContext().getResource("/WEB-INF/").getPath(); getRealPath()已经不推荐使用了。

tomcat的conf/server.xml的设置

69 <Connector port="8003" 70 protocol="org.apache.coyote.http11.Http11NioProtocol" 71 executor="tomcatThreadPool" 72 compression="on"
73 compressionMinSize="50" noCompressionUserAgents="gozilla, traviata" 74 compressableMimeType="application/json,text/xml,text/javascript,text/css,text/plain" 75 maxThreads="30000"
76 acceptCount="35000" 77 keepAliveTimeout="3000" 78 redirectPort="8443" 79 connectionTimeout="120000" 80 maxKeepAliveRequests="3000" 81 URIEncoding="UTF-8"

连接相关

maxThreads:最大线程数
minSpareThreads:初始化时创建的线程数
maxSpareThreads:一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。
acceptCount:允许最大连接数。指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中,请求数超过这个数的请求将不予处理
connectionTimeout:网络连接超时,0表示不超时。需要设置,一般为60000毫秒
enableLookups="false":不支持域名解析

压缩相关

compression:是否开启压缩
compressionMinSize:超过这个大小才启动压缩
noCompressionUserAgents:对这些userAgent不启用压缩
compressableMimeType:需要压缩的响应的格式,例如:text/html,apllication/json

tomcat的bin/catalina.xml的设置

JAVA_OPTS="$JAVA_OPTS -Xms4000m -Xmx16000m -Xmn8000m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -Dorg.apache.catalina.security.SecurityListener.UMASK=umask-Dcom.sun.management.jmxremote.port=11122 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=***.***.***.***"

这里的参数含义:

内存相关的设置

-server:一定要作为第一个参数,在多个CPU时性能佳。如果不配置该参数,JVM会根据应用服务器硬件配置自动选择不同模式,server模式启动比较慢,但是运行期速度得到了优化,适合于服务器端运行的JVM
-XmsIm:初始化的堆内存
-XmxMm:最大的推内存,整个JVM可用内存大小=青年代大小 + 老年代大小 + 持久代大小
-XX:NewRatio:控制默认的Young代的大小,例如,设置-XX:NewRatio=3意味着Young代和老年代的比率是1:3。换句话说,Eden和Survivor空间总和是整个堆大小的1/4
-XmnMm:设置青年代大小
-XX:MaxTenuringThreshold:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论
-XX:SurvivorRatio:设置年轻代中Eden区与Survivor区的大小比值。
-XX:PermSize:初始化永久内存区域大小,如果Xmx过大,占了全部的内存,那这个过小也会oom。这个在1.8中取消了。
-XX:MaxPermSize:设定最大内存的永久保存区域。这个在1.8中取消了。
-Xss:每个线程的Stack大小,用系统默认的就好了,这个可以不用设置
-Xlp:to specify that the heap should be allocated in large pages.
-XX:+UseBiasedLocking:启用一个优化了的线程锁,我们知道在我们的appserver,每个http请求就是一个线程,有的请求短有的请求长,就会有请求排队的现象,甚至还会出现线程阻塞,这个优化了的线程锁使得你的appserver内对线程处理自动进行最优调配

吞吐量优先的并行收集器:

-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

响应时间优先的并发收集器:

-XX:+UseConcMarkSweepGC:设置年老代为并发收集CMS
-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用
-XX:+UseCMSCompactAtFullCollection:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低
-XX:CMSFullGCsBeforeCompaction=5:此值设置运行多少次GC以后对内存空间进行压缩、整理。

GC辅助信息

GC基础: 主要算法有引用计数法,解决不了循环依赖的问题;标记清除法,会有空间碎片问题;复制算法,新生代串行垃圾回收器用了这种方法 ;标记压缩算法,在老年代可能会用到;
主要的思想还是分代。

虚拟机的类型:按线程数分,可以分为串行和并行垃圾回收器。按是否影响使用分,可以分为并发和独占式垃圾回收器。按碎片处理方式分,可以分为压缩式和非压缩式垃圾回收器。按工作的内存区间分,又分为新生代和老生代。

在Hot Spot虚拟机中,client模式下新生代默认是串行垃圾回收器(-XX:+UseSerialGC); 另外的一些设置:

-XX:+UseParNewGC(新生代使用并行收集器,老年代使用串行收集器serial-mark-sweep);

-XX:+UseParallelOldGC(对新生代和老年代都使用并行收集器,会附加默认设置-XX:+UseParallelGC);

-XX:+UseParNewGC & -XX:+UseConcMarkSweepGC(新生成新生代并行收集器,老年代用CMS

-XX:+UseParallelGC 与 -XX:+UseParallelOldGC不能同时与这个一起使用XX:+UseConcMarkSweepGC。

-verbose:gc:显示gc信息
-Xloggc:gc.log:指定垃圾收集日志文件
-XX:+PrintGC:打印gc信息
-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用。输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
-XX:+PrintGCApplicationConcurrentTime::打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用,输出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:PrintHeapAtGC:打印GC前后的详细堆栈信息


4. 用到的工具

  1. 自己写的打压的工具,解析log的工具。没有用jmeter是因为这个测试的场景是上下文相关的,与其慢慢配置不如直接开写。
  2. jvisualVm(分析线程,cpu,内存),jmap(内存),jconsole(类,内存分配)

5. 优化结果(未完待续...)

comments powered by Disqus