Jade Dungeon

Tomcat性能调优

找出对应用表现生成影响的阈值(threshold)在哪里。

初始化内存大小参数

export CATALINA_HOME=/opt/java/tomcat8/
export CATALINA_BASE=/home/deploy/workspace/tomcat/octopus-portal

JVM_MAX_MEM=$(free -m|awk '/Mem/{print$2-1300}')
JVM_AGG_MAX_MEM=$(echo $JVM_MAX_MEM |awk '{print $0 * 0.7}' | awk -F '.' '{print $1}' )
JVM_AGG_MIN_MEM=$(echo $JVM_MAX_MEM |awk '{print $0 * 0.5}' | awk -F '.' '{print $1}' )
JVM_AGG_NEW_MEM=$(echo $JVM_MAX_MEM |awk '{print $0 * 0.2}' | awk -F '.' '{print $1}' )

#########################################################
#JVM_AGG_MAX_MEM=2048
#JVM_AGG_MIN_MEM=1024
#JVM_AGG_NEW_MEM=512
##########################################################

MY_IP=$(ifconfig eth0 |awk -F '[ |:]+' '/inet /{print $4}')
[[ -z $MY_IP ]] && MY_IP=$(ifconfig bond0 |awk -F '[ |:]+' '/inet /{print $4}')

MY_PORT=10053


if [[ -z $JVM_MAX_MEM || $JVM_MAX_MEM -lt 1024 ]] ;then
        JVM_AGG_MAX_MEM=1024
        JVM_AGG_MIN_MEM=1024
        JVM_AGG_NEW_MEM=512
fi


export JAVA_OPTS="-server -XX:+AggressiveOpts -XX:+DoEscapeAnalysis \
                -Dcom.sun.management.jmxremote.port=${MY_PORT} \
                -Djava.rmi.server.hostname=${MY_IP} \
                -Dcom.sun.management.jmxremote.authenticate=false \
                -Dcom.sun.management.jmxremote.ssl=false  \
                -Xmx${JVM_AGG_MAX_MEM}m \
                -Xms${JVM_AGG_MIN_MEM}m \
                -Xmn${JVM_AGG_NEW_MEM}m \
                -Xss512K \
                -XX:PermSize=128M \
                -XX:MaxPermSize=256M \
                -XX:SurvivorRatio=8 \
                -XX:+UseCompressedOops \
                -XX:+UseParNewGC \
                -XX:+UseConcMarkSweepGC \
                -XX:+CMSClassUnloadingEnabled \
                -XX:+UseCMSCompactAtFullCollection \
                -XX:CMSFullGCsBeforeCompaction=0 \
                -XX:+CMSParallelRemarkEnabled \
                -XX:+DisableExplicitGC \
                -XX:+UseCMSInitiatingOccupancyOnly \
                -XX:CMSInitiatingOccupancyFraction=75 \
                -XX:SoftRefLRUPolicyMSPerMB=0 \
                                "

基准测试

测试工具发起请求的线程数量不应该大于Tomcats配置文件conf/server.xmlConnector配置最大线程数maxThread(默认设置是150)。

常用工具:

  • Apache ab:发起请求数量多,适合基准测试。
  • Siege:请求速度不如Apache ab,但可以在请求之间生成随机的空闲时间, 还可以从一组指定的URL中随机选择URL。
  • Apache Jakarta JMeter:耗资源大、速度不快,但功能强大。

用Apache ab进行基准调校

ab -k -n 100000 -c 149 http://localhost:8080
  • -c:并发线程数量。
  • -n:请求总数。
  • -k:保持联机keep-alive

Siege

siege -b -r 671 -c 149 localhost:8080
  • -b:进行基准测试,即在请求期间不等待。
  • -r:平均每个线程发起的请求数。
  • -c:并行的线程数。

这个例子大约发起\(671 \times 149 \approx 100000\)个请求。

Apache Jakarta JMeter

(略)

Tomcat Web Server性能比较

AJP协议是以TCP协议的形式把HTTP协议的请求转给另一个应用, 这样转比直接用HTTP转快。

Tomcat提供的连接器(Connector):

  • JIO(java.io)是由java.io包实现的HTTP和AJP协议。
  • NIO(java.nio)由java.nio包提供的非阻塞TCP连接实现, 以很少的线程实现大量的连接。
  • APR(Apache Portable Runtime)是由Java通过JNI调用libtcnative库。

作为对比,参照一下Apache httpd:

  • mod_jk:
  • mod_proxy_ajp:httpd用AJP协议把HTTP请求转给Tomcat。
  • mod_proxy_http:httpd用HTTP协议把HTTP请求转给Tomcat。

服务器配置

Tomcat 的JVM启动参数:

-Xms384M -Xmx384M -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true

不同连接器的配置:

HTTP JIO连接器:

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

HTTP APR连接器:

<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol"
	enableLookups="false" connectionTimeout="20000"
	redirectPort-"8443" />

HTTP NIO连接器:

<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
	maxThreads="150" connectionTimeout="20000"
	redirectPort-"8443" />

AJP JIO/APR连接器,由设置LD_LIBRARY_PATH予以切换:

<Connector port="8009" protocol="AJP/1.3" redirectPort-"8443" />

AJP NIO连接器:

<Connector port="0" protocol="AJP/1.3" 
	channelNioSocket.port="8009"
	channelNioSocket.maxThreads="150"
	channelNioSocket.maxSpareThreads="50"
	channelNioSocket.maxSpareThreads="25"
	channelNioSocket.bufferSize="16384" />

结果分析

(略)

内部调校

停用DNS查询

停用以后,程序中调用getRemoteHost()时只会取得IP地址,而不是域名。

server.xml中配置Connector对象:

<Connector enableLookups="false" />

调整线程数

  • minThreads:对应访问量低谷,比如低谷时每秒5次访问,每次1秒完成请求。 则最小线程数分配5个就够了。
  • maxThreads:对应最大并发,设置限制以适应JVM最大内存限制,防止DoS攻击。

这两个数的设置没有现成的规则,尝试不同的值模拟网络流量。

预编译JSP

把JSP当作servlet配置在web.xml里,启动序编译一遍:

<servlet>
	<servlet-name>index.jsp</servlet-name>
	<jsp-file>/index.jsp</jsp-file>
	<load-on-startup>0</load-on-startup>
</servlet>