Tomcat jsvc 调优及JMX监控
实验背景
======================================================
系统版本:CentOS release 6.5 (Final)
Tomcat版本: Apache-tomcat-7.0.54
Tomcat介绍:
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。目前最新版本是8.0。
======================================================
Tomcat 7最新版本下载链接: http://mirrors.cnnic.cn/apache/tomcat/tomcat-7/v7.0.56/bin/apache-tomcat-7.0.56.tar.gz
Tomcat 安装: tar zxvf apache-tomcat-7.0.54.tar.gz -C /opt/
jsvc编译及调优: cd apache-tomcat-7.0.54/bin tar zxvf commons-daemon-native.tar.gz cd commons-daemon-1.0.x-native-src/unix ./configure make -j4 cp jsvc ../../ cd ../../
添加tomcat用户,后面用来启动tomcat进程: useradd -s /sbin/nologin tomcat
jsvc help文件: # export JAVA_HOME=/opt/jdk1.7.0_60/ # ./jsvc --help Usage: jsvc [-options] class [args...] Where options include: -help | --help | -? show this help page (implies -nodetach) -jvmuse a specific Java Virtual Machine. Available JVMs: 'server' -client use a client Java Virtual Machine. -server use a server Java Virtual Machine. -cp | -classpath set search path for service classes and resouces -java-home | -home set the path of your JDK or JRE installation (or set the JAVA_HOME environment variable) -version show the current Java environment version (to check correctness of -home and -jvm. Implies -nodetach) -showversion show the current Java environment version (to check correctness of -home and -jvm) and continue execution. -nodetach don't detach from parent process and become a daemon -debug verbosely print debugging information -check only check service (implies -nodetach) -user user used to run the daemon (defaults to current user) -verbose[:class|gc|jni] enable verbose output -cwd set working directory to given location (defaults to /) -outfile Location for output from stdout (defaults to /dev/null) Use the value '&2' to simulate '1>&2' -errfile Location for output from stderr (defaults to /dev/null) Use the value '&1' to simulate '2>&1' -pidfile Location for output from the file containing the pid of jsvc (defaults to /var/run/jsvc.pid) -D = set a Java system property -X
=============================================================
接下来是重点了!
复制bin目录下的daemon.sh 生成初步service文件 # cp ./daemon.sh /etc/init.d/tomcat # chmod +x /etc/init.d/tomcat
jsvc 调优参数: export PATH=/bin:/sbin:/usr/bin:/usr/sbin export JAVA_HOME=/opt/jdk1.7.0_60 export JRE_HOME=/opt/jdk1.7.0_60/jre export CATALINA_BASE=/opt/apache-tomcat-7.0.54 export CATALINA_HOME=/opt/apache-tomcat-7.0.54 export CATALINA_PID=$CATALINA_BASE/logs/catalina-daemon.pid export CATALINA_TMP=$CATALINA_BASE/temp export TOMCAT_USER=tomcat export CATALINA_OPTS="-server -Xss512k -Xms2048M -Xmx2048M -XX:MaxPermSize=256M -XX:PermSize=128M -XX:NewSize=128M -XX:+CMSIncreme ntalMode -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=8 -Djavax.servlet.r equest.encoding=UTF-8 -Djavax.servlet.response.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -Dcom.sun.manage ment.jmxremote -Dcom.sun.management.jmxremote.port=100861 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.a uthenticate=false -Djava.rmi.server.hostname=192.168.3.5"
-XX:+CMSIncrementalMode 该标志将开启CMS收集器的增量模式。增量模式经常暂停CMS过程,以便对应用程序线程作出完全的让步。因此,收集器将花更长的时间完成整个收集周期。因此,只有通过测试后发现正常CMS周期对应用程序线程干扰太大时,才应该使用增量模式。由于现代服务器有足够的处理器来适应并发的垃圾收集,所以这种情况发生得很少。
-XX:CMSInitiatingOccupancyFraction 当堆满之后,并行收集器便开始进行垃圾收集,例如,当没有足够的空间来容纳新分配或提升的对象。对于CMS收集器,长时间等待是不可取的,因为在并发垃圾收集期间应用持续在运行(并且分配对象)。因此,为了在应用程序使用完内存之前完成垃圾收集周期,CMS收集器要比并行收集器更先启动。 因为不同的应用会有不同对象分配模式,JVM会收集实际的对象分配(和释放)的运行时数据,并且分析这些数据,来决定什么时候启动一次CMS垃圾收集周期。为了引导这一过程, JVM会在一开始执行CMS周期前作一些线索查找。该线索由 -XX:CMSInitiatingOccupancyFraction=来设置,该值代表老年代堆空间的使用率。比如,value=75意味着第一次CMS垃圾收集会在老年代被占用75%时被触发。通常CMSInitiatingOccupancyFraction的默认值为68(之前很长时间的经历来决定的)。
-XX:+UseConcMarkSweepGC 该标志首先是激活CMS收集器。默认HotSpot JVM使用的是并行收集器。
-XX:UseParNewGC 当使用CMS收集器时,该标志激活年轻代使用多线程并行执行垃圾回收。这令人很惊讶,我们不能简单在并行收集器中重用-XX:UserParNewGC标志,因为概念上年轻代用的算法是一样的。然而,对于CMS收集器,年轻代GC算法和老年代GC算法是不同的,因此年轻代GC有两种不同的实现,并且是两个不同的标志。 注意最新的JVM版本,当使用-XX:+UseConcMarkSweepGC时,-XX:UseParNewGC会自动开启。因此,如果年轻代的并行GC不想开启,可以通过设置-XX:-UseParNewGC来关掉。
XX:ConcGCThreads 标志-XX:ConcGCThreads=(早期JVM版本也叫-XX:ParallelCMSThreads)定义并发CMS过程运行时的线程数。比如value=4意味着CMS周期的所有阶段都以4个线程来执行。尽管更多的线程会加快并发CMS过程,但其也会带来额外的同步开销。因此,对于特定的应用程序,应该通过测试来判断增加CMS线程数是否真的能够带来性能的提升。 如果还标志未设置,JVM会根据并行收集器中的-XX:ParallelGCThreads参数的值来计算出默认的并行CMS线程数。该公式是ConcGCThreads = (ParallelGCThreads + 3)/4。因此,对于CMS收集器, -XX:ParallelGCThreads标志不仅影响“stop-the-world”垃圾收集阶段,还影响并发阶段。 总之,有不少方法可以配置CMS收集器的多线程执行。正是由于这个原因,建议第一次运行CMS收集器时使用其默认设置, 然后如果需要调优再进行测试。只有在生产系统中测量(或类生产测试系统)发现应用程序的暂停时间的目标没有达到 , 就可以通过这些标志应该进行GC调优。
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
接下来这些参数 是针对于 JMX 远程监控的: -Dcom.sun.management.jmxremote 启用JMX远程监控 -Dcom.sun.management.jmxremote.port=100861 使用端口100861 -Dcom.sun.management.jmxremote.authenticate=false 远程连接不需要密码认证 -Dcom.sun.management.jmxremote.ssl=false 不使用SSL -Dcom.sun.management.jmxremote.access.file=$CATALINA_HOME/conf/jmxremote.access 使用指定的JMX帐号授权文件 -Dcom.sun.management.jmxremote.password.file=$CATALINA_HOME/conf/jmxremote.password 使用指定的JMX帐号文件
=============================================================
到这里为止 tomcat 针对jsvc 调优已经差不多了,下面启动tomcat 测试:
# /etc/init.d/tomcat start
# ps -ef |grep tomcat root 12166 1 0 16:09 ? 00:00:00 jsvc.exec -java-home /opt/jdk1.7.0_60 -user tomcat -pidfile /opt/apache-tomcat-7.0.54/logs/catalina-daemon.pid -wait 10 -outfile /opt/apache-tomcat-7.0.54/logs/catalina-daemon.out -errfile &1 -classpath /opt/apache-tomcat-7.0.54/bin/bootstrap.jar:/opt/apache-tomcat-7.0.54/bin/commons-daemon.jar:/opt/apache-tomcat-7.0.54/bin/tomcat-juli.jar -Djava.util.logging.config.file=/opt/apache-tomcat-7.0.54/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xss512k -Xms2048M -Xmx2048M -XX:MaxPermSize=256M -XX:PermSize=128M -XX:NewSize=128M -XX:+CMSIncrementalMode -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=8 -Djavax.servlet.request.encoding=UTF-8 -Djavax.servlet.response.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10086 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.3.5 -Djava.endorsed.dirs= -Dcatalina.base=/opt/apache-tomcat-7.0.54 -Dcatalina.home=/opt/apache-tomcat-7.0.54 -Djava.io.tmpdir=/opt/apache-tomcat-7.0.54/temp org.apache.catalina.startup.Bootstrap tomcat 12167 12166 34 16:09 ? 00:00:05 jsvc.exec -java-home /opt/jdk1.7.0_60 -user tomcat -pidfile /opt/apache-tomcat-7.0.54/logs/catalina-daemon.pid -wait 10 -outfile /opt/apache-tomcat-7.0.54/logs/catalina-daemon.out -errfile &1 -classpath /opt/apache-tomcat-7.0.54/bin/bootstrap.jar:/opt/apache-tomcat-7.0.54/bin/commons-daemon.jar:/opt/apache-tomcat-7.0.54/bin/tomcat-juli.jar -Djava.util.logging.config.file=/opt/apache-tomcat-7.0.54/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xss512k -Xms2048M -Xmx2048M -XX:MaxPermSize=256M -XX:PermSize=128M -XX:NewSize=128M -XX:+CMSIncrementalMode -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=8 -Djavax.servlet.request.encoding=UTF-8 -Djavax.servlet.response.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10086 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.3.5 -Djava.endorsed.dirs= -Dcatalina.base=/opt/apache-tomcat-7.0.54 -Dcatalina.home=/opt/apache-tomcat-7.0.54 -Djava.io.tmpdir=/opt/apache-tomcat-7.0.54/temp org.apache.catalina.startup.Bootstrap
如果你的系统开起了iptables的话,这里需要开放针对jmx 和 tomcat 的端口: vim /etc/sysconfig/iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 8080 -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 10086 -j ACCEPT /etc/init.d/iptables restart 重启防火墙生效;
接下来打开浏览器测试tomcat是否已经work; http://192.168.3.5:8080
=============================================================
这里我们验证一下JMX远程监控,推荐使用VisualVM监控;
JDK中还藏着一个宝贝,它的名字叫做VisualVM。VisualVM是Sun的一个OpenJDK项目,其目的在于为Java应用创建一个整套的问题解决工具;
官网主页: http://visualvm.java.net/
步骤如下:
1)添加远程主机:
2)添加jmx连接:
这里需要注意格式:主机+端口 ,另外下面勾选 不要求SSL连接;
3)添加完成之后打开查看tomcat 资源使用情况;
添加成功之后下面有个jmx的主机连接:
打开即可!
So Next ,Enjoy it!
下面是一个优化后的tomcat启动脚本的范例,可以放置在/etc/init.d/下 使用, start 、 stop等进行启动 、关闭操作;
#!/bin/sh # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # ----------------------------------------------------------------------------- # Scripts for tomcat based on jsvc # ----------------------------------------------------------------------------- # # resolve links - $0 may be a softlink export PATH=/bin:/sbin:/usr/bin:/usr/sbin export JAVA_HOME=/opt/jdk1.7.0_60 export JRE_HOME=/opt/jdk1.7.0_60/jre export CATALINA_BASE=/opt/apache-tomcat-7.0.54 export CATALINA_HOME=/opt/apache-tomcat-7.0.54 export CATALINA_PID=$CATALINA_BASE/logs/catalina-daemon.pid export CATALINA_TMP=$CATALINA_BASE/temp export TOMCAT_USER=tomcat export CATALINA_OPTS="-server -Xss512k -Xms2048M -Xmx2048M -XX:MaxPermSize=256M -XX:PermSize=128M -XX:NewSize=128M -XX:+CMSIncrementalMode -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=8 -Djavax.servlet.request.encoding=UTF-8 -Djavax.servlet.response.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Shanghai -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=22222 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.3.5" ARG0="$0" while [ -h "$ARG0" ]; do ls=`ls -ld "$ARG0"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then ARG0="$link" else ARG0="`dirname $ARG0`/$link" fi done DIRNAME="`dirname $ARG0`" PROGRAM="`basename $ARG0`" while [ ".$1" != . ] do case "$1" in --java-home ) JAVA_HOME="$2" shift; shift; continue ;; --catalina-home ) CATALINA_HOME="$2" shift; shift; continue ;; --catalina-base ) CATALINA_BASE="$2" shift; shift; continue ;; --catalina-pid ) CATALINA_PID="$2" shift; shift; continue ;; --tomcat-user ) TOMCAT_USER="$2" shift; shift; continue ;; * ) break ;; esac done # OS specific support (must be 'true' or 'false'). cygwin=false; darwin=false; case "`uname`" in CYGWIN*) cygwin=true ;; Darwin*) darwin=true ;; esac # Use the maximum available, or set MAX_FD != -1 to use that test ".$MAX_FD" = . && MAX_FD="maximum" # Setup parameters for running the jsvc # test ".$TOMCAT_USER" = . && TOMCAT_USER=tomcat # Set JAVA_HOME to working JDK or JRE # JAVA_HOME=/opt/jdk-1.6.0.22 # If not set we'll try to guess the JAVA_HOME # from java binary if on the PATH # if [ -z "$JAVA_HOME" ]; then JAVA_BIN="`which java 2>/dev/null || type java 2>&1`" test -x "$JAVA_BIN" && JAVA_HOME="`dirname $JAVA_BIN`" test ".$JAVA_HOME" != . && JAVA_HOME=`cd "$JAVA_HOME/.." >/dev/null; pwd` else JAVA_BIN="$JAVA_HOME/bin/java" fi # Only set CATALINA_HOME if not already set test ".$CATALINA_HOME" = . && CATALINA_HOME=`cd "$DIRNAME/.." >/dev/null; pwd` test ".$CATALINA_BASE" = . && CATALINA_BASE="$CATALINA_HOME" test ".$CATALINA_MAIN" = . && CATALINA_MAIN=org.apache.catalina.startup.Bootstrap # If not explicitly set, look for jsvc in CATALINA_BASE first then CATALINA_HOME if [ -z $JSVC ]; then JSVC="$CATALINA_BASE/bin/jsvc" if [ ! -x $JSVC ]; then JSVC="$CATALINA_HOME/bin/jsvc" fi fi # Set the default service-start wait time if necessary test ".$SERVICE_START_WAIT_TIME" = . && SERVICE_START_WAIT_TIME=10 # Ensure that any user defined CLASSPATH variables are not used on startup, # but allow them to be specified in setenv.sh, in rare case when it is needed. CLASSPATH= JAVA_OPTS= if [ -r "$CATALINA_BASE/bin/setenv.sh" ]; then . "$CATALINA_BASE/bin/setenv.sh" elif [ -r "$CATALINA_HOME/bin/setenv.sh" ]; then . "$CATALINA_HOME/bin/setenv.sh" fi # Add on extra jar files to CLASSPATH test ".$CLASSPATH" != . && CLASSPATH="${CLASSPATH}:" CLASSPATH="$CLASSPATH$CATALINA_HOME/bin/bootstrap.jar:$CATALINA_HOME/bin/commons-daemon.jar" test ".$CATALINA_OUT" = . && CATALINA_OUT="$CATALINA_BASE/logs/catalina-daemon.out" test ".$CATALINA_TMP" = . && CATALINA_TMP="$CATALINA_BASE/temp" # Add tomcat-juli.jar to classpath # tomcat-juli.jar can be over-ridden per instance if [ -r "$CATALINA_BASE/bin/tomcat-juli.jar" ] ; then CLASSPATH="$CLASSPATH:$CATALINA_BASE/bin/tomcat-juli.jar" else CLASSPATH="$CLASSPATH:$CATALINA_HOME/bin/tomcat-juli.jar" fi # Set juli LogManager config file if it is present and an override has not been issued if [ -z "$LOGGING_CONFIG" ]; then if [ -r "$CATALINA_BASE/conf/logging.properties" ]; then LOGGING_CONFIG="-Djava.util.logging.config.file=$CATALINA_BASE/conf/logging.properties" else # Bugzilla 45585 LOGGING_CONFIG="-Dnop" fi fi test ".$LOGGING_MANAGER" = . && LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager" JAVA_OPTS="$JAVA_OPTS $LOGGING_MANAGER" # Set -pidfile test ".$CATALINA_PID" = . && CATALINA_PID="$CATALINA_BASE/logs/catalina-daemon.pid" # Increase the maximum file descriptors if we can if [ "$cygwin" = "false" ]; then MAX_FD_LIMIT=`ulimit -H -n` if [ "$?" -eq 0 ]; then # Darwin does not allow RLIMIT_INFINITY on file soft limit if [ "$darwin" = "true" -a "$MAX_FD_LIMIT" = "unlimited" ]; then MAX_FD_LIMIT=`/usr/sbin/sysctl -n kern.maxfilesperproc` fi test ".$MAX_FD" = ".maximum" && MAX_FD="$MAX_FD_LIMIT" ulimit -n $MAX_FD if [ "$?" -ne 0 ]; then echo "$PROGRAM: Could not set maximum file descriptor limit: $MAX_FD" fi else echo "$PROGRAM: Could not query system maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # ----- Execute The Requested Command ----------------------------------------- case "$1" in run ) shift "$JSVC" $* \ $JSVC_OPTS \ -java-home "$JAVA_HOME" \ -pidfile "$CATALINA_PID" \ -wait 10 \ -nodetach \ -outfile "&1" \ -errfile "&2" \ -classpath "$CLASSPATH" \ "$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \ -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \ -Dcatalina.base="$CATALINA_BASE" \ -Dcatalina.home="$CATALINA_HOME" \ -Djava.io.tmpdir="$CATALINA_TMP" \ $CATALINA_MAIN exit $? ;; start ) "$JSVC" $JSVC_OPTS \ -java-home "$JAVA_HOME" \ -user $TOMCAT_USER \ -pidfile "$CATALINA_PID" \ -wait 10 \ -outfile "$CATALINA_OUT" \ -errfile "&1" \ -classpath "$CLASSPATH" \ "$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \ -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \ -Dcatalina.base="$CATALINA_BASE" \ -Dcatalina.home="$CATALINA_HOME" \ -Djava.io.tmpdir="$CATALINA_TMP" \ $CATALINA_MAIN exit $? ;; stop ) "$JSVC" $JSVC_OPTS \ -stop \ -pidfile "$CATALINA_PID" \ -classpath "$CLASSPATH" \ -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" \ -Dcatalina.base="$CATALINA_BASE" \ -Dcatalina.home="$CATALINA_HOME" \ -Djava.io.tmpdir="$CATALINA_TMP" \ $CATALINA_MAIN exit $? ;; version ) "$JSVC" \ -java-home "$JAVA_HOME" \ -pidfile "$CATALINA_PID" \ -classpath "$CLASSPATH" \ -errfile "&2" \ -version \ -check \ $CATALINA_MAIN if [ "$?" = 0 ]; then "$JAVA_BIN" \ -classpath "$CATALINA_HOME/lib/catalina.jar" \ org.apache.catalina.util.ServerInfo fi exit $? ;; * ) echo "Unknown command: \`$1'" echo "Usage: $PROGRAM ( commands ... )" echo "commands:" echo " run Start Tomcat without detaching from console" echo " start Start Tomcat" echo " stop Stop Tomcat" echo " version What version of commons daemon and Tomcat" echo " are you running?" exit 1 ;; esac
=============================================================
参考资料: http://tomcat.apache.org/tomcat-7.0-doc/setup.html#Unix_daemon http://tomcat.apache.org/tomcat-7.0-doc/RUNNING.txt https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk/bin/daemon.sh http://ifeve.com/useful-jvm-flags-part-7-cms-collector/
=========================================================