Tomcat 性能监控与优化
JMX
JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的 代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理。可以 利用JDK的JConsole来访问Tomcat JMX接口实施监控,具体步骤如下: 首先,打开tomcat5的bin目录中的catalina.bat文件,在头部注释部分的后面加上: set JAVA_OPTS=%JAVA_OPTS% ‐Dcom.sun.management.jmxremote.port=8999 ‐Dcom.sun.management.jmxremote.authenticate=false ‐Dcom.sun.management.jmxremote.ssl=false 如果已经配置好,则可使用 JConsole 打开监控平台查看 Tomcat 性能。
Linux 下配置 catalina.sh 的例子: JAVA_OPTS='-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.0.106'
由于 JMX 提供的接口是任何 Java 程序都可以调用访问的,因此我们可以编写 JAVA 程序来收 集 Tomcat 性能数据,具体代码如下所示: import java.lang.management.MemoryUsage; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Formatter; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo; import javax.management.MBeanServerConnection; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.openmbean.CompositeDataSupport; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL;
public class MonitorTomcat { /** * @param args */ public static void main(String[] args) { try { String jmxURL = "service:jmx:rmi:///jndi/rmi://192.168.0.106:8999/jmxrmi";//tomcat jmx url JMXServiceURL serviceURL = new JMXServiceURL(jmxURL); Map map = new HashMap(); String[] credentials = new String[] { "monitorRole" , "QED" }; map.put("jmx.remote.credentials", credentials); JMXConnector connector = JMXConnectorFactory.connect(serviceURL, map); MBeanServerConnection mbsc = connector.getMBeanServerConnection();
ObjectName threadObjName = new ObjectName("Catalina:type=ThreadPool,name=http-8080"); MBeanInfo mbInfo = mbsc.getMBeanInfo(threadObjName); String attrName = "currentThreadCount"; //tomcat 的线程数对应的属性值 MBeanAttributeInfo[] mbAttributes = mbInfo.getAttributes(); System.out.println("currentThreadCount:"+mbsc.getAttribute(threadObjName, attrName)); //heap for(int j=0;j <mbsc.getDomains().length;j++){ System.out.println("###########"+mbsc.getDomains()[j]); } Set MBeanset = mbsc.queryMBeans(null, null); System.out.println("MBeanset.size() : " + MBeanset.size()); Iterator MBeansetIterator = MBeanset.iterator(); while (MBeansetIterator.hasNext()) { ObjectInstance objectInstance = (ObjectInstance)MBeansetIterator.next(); ObjectName objectName = objectInstance.getObjectName(); String canonicalName = objectName.getCanonicalName(); System.out.println("canonicalName : " + canonicalName); if (canonicalName.equals("Catalina:host=localhost,type=Cluster")) { // Get details of cluster MBeans System.out.println("Cluster MBeans Details:"); System.out.println("=========================================");
//getMBeansDetails(canonicalName); String canonicalKeyPropList = objectName.getCanonicalKeyPropertyListString(); } } //‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ system ‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ObjectName runtimeObjName = new ObjectName("java.lang:type=Runtime"); System.out.println("厂商:"+ (String)mbsc.getAttribute(runtimeObjName, "VmVendor")); System.out.println("程序:"+ (String)mbsc.getAttribute(runtimeObjName, "VmName")); System.out.println("版本:"+ (String)mbsc.getAttribute(runtimeObjName, "VmVersion")); Date starttime=new Date((Long)mbsc.getAttribute(runtimeObjName, "StartTime")); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("启动时间:"+df.format(starttime)); Long timespan=(Long)mbsc.getAttribute(runtimeObjName, "Uptime"); System.out.println("连续工作时间:"+MonitorTomcat.formatTimeSpan(timespan)); //-------------------------JVM--------------------------------- //堆使用率 ObjectName heapObjName = new ObjectName("java.lang:type=Memory");
MemoryUsage heapMemoryUsage = MemoryUsage.from((CompositeDataSupport)mbsc.getAttribute(heapObjName, "HeapMemoryUsage")); long maxMemory = heapMemoryUsage.getMax(); //堆最大 long commitMemory = heapMemoryUsage.getCommitted(); //堆当前分配 long usedMemory = heapMemoryUsage.getUsed(); System.out.println("heap:"+(double)usedMemory*100/commitMemory+"%"); // 堆使用率
MemoryUsage nonheapMemoryUsage = MemoryUsage.from((CompositeDataSupport)mbsc.getAttribute(heapObjName, "NonHeapMemoryUsage")); long noncommitMemory = nonheapMemoryUsage.getCommitted(); long nonusedMemory = heapMemoryUsage.getUsed(); System.out.println("nonheap:"+(double)nonusedMemory*100/noncommitMemory+"%"); ObjectName permObjName = new ObjectName("java.lang:type=MemoryPool,name=Perm Gen");
MemoryUsage permGenUsage = MemoryUsage.from((CompositeDataSupport)mbsc.getAttribute(permObjName, "Usage")); long committed = permGenUsage.getCommitted();//持久堆大小 long used = heapMemoryUsage.getUsed();// System.out.println("perm gen:"+(double)used*100/committed+"%");//持久堆使用率 //‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ Session ‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ObjectName managerObjName = new ObjectName("Catalina:type=Manager,*");
PrefTest 性能测试工作室 - http://cnblogs.com/preftest 7
PrefTest 性能测试工作室 - http://cnblogs.com/preftest
Set<ObjectName> s=mbsc.queryNames(managerObjName, null); for (ObjectName obj:s){ System.out.println("应用名:"+obj.getKeyProperty("path")); ObjectName objname=new ObjectName(obj.getCanonicalName()); System.out.println(" 最大会话数:"+ mbsc.getAttribute( objname, "maxActiveSessions")); System.out.println("会话数:"+ mbsc.getAttribute( objname, "activeSessions")); System.out.println("活动会话数:"+ mbsc.getAttribute( objname, "sessionCounter")); } //‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ Thread Pool ‐‐‐‐‐‐‐‐‐‐‐‐‐‐ ObjectName threadpoolObjName = new ObjectName("Catalina:type=ThreadPool,*"); Set<ObjectName> s2=mbsc.queryNames(threadpoolObjName, null); for (ObjectName obj:s2){ System.out.println("端口名:"+obj.getKeyProperty("name")); ObjectName objname=new ObjectName(obj.getCanonicalName()); System.out.println("最大线程数:"+ mbsc.getAttribute( objname, "maxThreads")); System.out.println(" 当前线程数:"+ mbsc.getAttribute( objname, "currentThreadCount")); System.out.println(" 繁忙线程数:"+ mbsc.getAttribute( objname, "currentThreadsBusy")); } } catch (Exception e) { e.printStackTrace(); } }
public static String formatTimeSpan(long span){ long minseconds = span % 1000; span = span /1000; long seconds = span % 60; span = span / 60; long mins = span % 60; span = span / 60; long hours = span % 24; span = span / 24; long days = span; return (new Formatter()).format("%1$d 天 %2$02d:%3$02d:%4$02d.%5$03d", days,hours,mins,seconds,minseconds).toString(); } }
PrefTest 性能测试工作室 - http://cnblogs.com/preftest 8
PrefTest 性能测试工作室 - http://cnblogs.com/preftest
内存使用调整(Out of Memery 问题)
Tomcat 默认可以使用的内存为 128MB ,在较大型的应用项目中,这点内存是不够的,需 要调大。
Windows 下,在文件 {tomcat_home}/bin/catalina.bat , Unix 下,在文件
{tomcat_home}/bin/catalina.sh 的前面,增加如下设置: JAVA_OPTS='-Xms【初始化内存大小】 -Xmx【可以使用的最大内存】 '
需要把这个两个参数值调大。例如: rem ----- Execute The Requested Command --------------------------------------- set JAVA_OPTS='-Xms256m -Xmx512m'
表示初始化内存为 256MB ,可以使用的最大内存为 512MB
可用 JDK 附带的 Jconsole 查看 tomcat 的内存使用情况。
连接线程数调整(cannot connect to server 问题)
优化 tomcat 配置:maxThreads="500" minSpareThreads="400" maxSpareThreads="450"。
maxThreads This is the maximum number of threads allowed. This defines the upper bound to the concurrency, as Tomcat will not create any more threads than this. If there are more than maxThreads requests, they will be queued until the number of threads decreases. Increasing maxThreads increases the capability of Tomcat to handle more connections concurrently. However,threads use up system resources. Thus, setting a very high value might degrade performance, and could even cause Tomcat to crash. It is better to deny some incoming connections, rather that affect the ones that are being currently serviced.
maxSpareThreads This is the maximum number of idle threads allowed. Any excess idle threads are shut down by Tomcat. Setting this to a large value is not good for performance; the default (50) usually works for most Web sites with an average load. The value of maxSpareThreads should be greater than minSpareThreads, but less than maxThreads.
minSpareThreads This is the minimum number of idle threads allowed. On Tomcat startup, this is also the number of threads created when the Connector is initialized.If the number of idle threads falls below minSpareThreads, Tomcat creates new threads. Setting this to a large value is not good for performance, as each thread uses up resources. The default (4) usually works for most Web sites with an average load. Typically, sites with “bursty” traffic would need higher values for
minSpareThreads.
Tomcat6 使用默认的配置,在进行压力测试时,老是报错,用 Jmeter 模拟 40 个用户并发时, 正常,当超过 60 个时,居然全部报错,打开 server.xml 可以看到如下配置: <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> Tomcat 的官方网站的解释如下, maxThread 如果没有 set,默认值为 200. The maximum number of request processing threads to be created by this Connector , which therefore determines the maximum number of simultaneous requests that can be handled. If not specified, this attribute is set to 200. If an executor is associated with this connector, this attribute is ignored as the connector will execute tasks using the executor rather than an internal thread pool. 这个就产生问题了,打开 Tomcat 的源码发现默认值是 40,而不是 200,所以压力测试时遇 到同类的问题,大家一定要注意,添加以下配置: <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxThreads="150"/>
本文来自博客园,作者:up~up,转载请注明原文链接:https://www.cnblogs.com/soft-engineer/p/15066058.html
浙公网安备 33010602011771号