1.说明
在项目中,如果要去监控某个java应用的性能,这个时候有多个方法。你可以应用开源的软件或自己写个程序来监控。如果是自己写个程序来监控java的应用,我们就要考虑用什么样的方法是最好的。其中有两个方法:
第一种方法,你可以使用Sigar将要监控的代码写到自己的应用中,但是前提是这个应用是你自己公司开发的,如果要监控其它公司的应用,人家可能就不太愿意开放这个接口给你。
第二种方法,就是使用JMX去监控应用。笔者还是提倡使用这种方法。不过,在本博客中,笔者两种方法都会介绍怎么使用。
2.使用Sigar监控应用
使用Sigar监控应用时,需要将监控的代码写入到被监控的程序中,然后被监控的程序还要提供接口给监控系统去调用。这个有很大的缺陷,那就是被监控的应用必须是自己开发的,不然后要去监控别人的应用,可能会比较难。使用Sigar监控系统方法不难,主要是调用Sigar提供的接口即可。注意这里需要引入sigar.jar文件。
public class SigarTest {
/**
* main(这里用一句话描述这个方法的作用)
* @Title: main
* @Description: TODO
* @param @param args 设定文件
* @return void 返回类型
* @throws
*/
public static void main(String[] args) {
Sigar sigar = new Sigar();
DecimalFormat df = new DecimalFormat("######0.00");
try{
Mem mem = sigar.getMem();
CpuPerc cpuCerc = sigar.getCpuPerc();
Swap swap = sigar.getSwap();
System.out.println("*****当前CPU使用情况 :");
System.out.println("#总使用率: " + df.format(cpuCerc.getCombined() * 100) + "%");
// System.out.println("#用户使用率(user): " + cpuCerc.getUser() * 100 + "%");
// System.out.println("#系统使用率(sys): " + cpuCerc.getSys() * 100 + "%");
// System.out.println("#当前空闲率(idel): " + cpuCerc.getIdle() * 100 + "%");
System.out.println("\n*****当前内存使用情况 :");
System.out.println("#内存总量:" + mem.getTotal() / 1024 / 1024 /1024 + "G");
System.out.println("#已使用内存:" + mem.getUsed() / 1024 / 1024 /1024 + "G");
double totalMemory = mem.getTotal();
double usedMemory = mem.getUsed();
System.out.println("******" + usedMemory + "****" + totalMemory + "**" + df.format((usedMemory/totalMemory) * 100));
String usedMemoryRate = df.format((usedMemory/totalMemory) * 100);
System.out.println("******内存使用率:" +usedMemoryRate+"%");
// System.out.println("#剩余内存:" + mem.getFree() / 1024 / 1024 + "M");
System.out.println("\n*****虚拟内存使用情况:");
System.out.println("使用Swap内存使用大小:" + swap.getUsed()/ 1024 / 1024/ 1024 + "G");
System.out.println("使用Swap内存总大小:" + swap.getTotal()/ 1024 / 1024/ 1024 + "G");
double swapTotal = swap.getTotal();
double swapUsed = swap.getUsed();
String usedSwapRate = df.format((swapUsed / swapTotal) * 100);
System.out.println("Swap使用率:" + usedSwapRate + "%");
file();
}catch (Exception e) {
e.printStackTrace();
}
}
private static void file() throws Exception {
Sigar sigar = new Sigar();
FileSystem fslist[] = sigar.getFileSystemList();
for (int i = 0; i < fslist.length; i++) {
//System.out.println("分区的盘符名称" + i);
FileSystem fs = fslist[i];
FileSystemUsage usage = null;
usage = sigar.getFileSystemUsage(fs.getDirName());
switch (fs.getType()) {
case 0: // TYPE_UNKNOWN :未知
break;
case 1: // TYPE_NONE
break;
case 2: // TYPE_LOCAL_DISK : 本地硬盘
// 文件系统已经使用量
System.out.println(fs.getDevName() + "已经使用量: " + usage.getUsed()/1024/1024 + "G");
break;
case 3:// TYPE_NETWORK :网络
break;
case 4:// TYPE_RAM_DISK :闪存
break;
case 5:// TYPE_CDROM :光驱
break;
case 6:// TYPE_SWAP :页面交换
break;
}
}
return;
}
}
3.使用JMX监控应用
使用JMX监控应用的话,如果是Tomcat的项目,需要在Tomcat的bin\catalina.bat配置一些属性。如果是自己的应用,那么需要在自己的启动脚本中添加一些属性配置。博客主是讲解Tomcat的应用,换句话说就是监控Tomcat了。
3.1配置catalina文件
- 在windows下,需要配置catalina.bat的文件。配置以下的一行代码:
set CATALINA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8787 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
这里是没有用户和密码验证的。如果需要用户和密码验证,可以参考:https://www.cnblogs.com/liu-ke/p/7008920.html。那行代码配置的位置大概是在rem ----- Execute The Requested Command ---------------------------------------
这个说明的上方。
- 在linux的目录下,需要配置catalina.sh,配置的信息如下:
- CATALINA_OPTS=" $CATALINA_OPTS -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=10.25.25.177 -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
配置的大概位置在# ----- Execute The Requested Command -----------
这行说明上方。
- 配置好了之后,使用jdk\bin\jconsole.exe的软件来测试看看是否可以连接。
连接上之后,将会看到如下的页面:
3.2代码实现
package com.owen.tomcat.monitor.jmx;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
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 JMXTest {
//具体格式查看相关的API
private final static String SERVICE_1 = "service:jmx:rmi:///jndi/rmi://128.25.25.175:8999/jmxrmi";
public static void main(String[] args) throws IOException,
MalformedObjectNameException, NullPointerException {
Timer timer = new Timer();
timer.schedule(new JMXTest().new MonitorTask(SERVICE_1), 0, 10000);
}
private class MonitorTask extends TimerTask{
private String service ;
public MonitorTask(String service){
this.service = service;
}
@Override
public void run() {
JMXmonitor(service);
}
}
private static void JMXmonitor(String service) {
JMXConnector connector = null;
try {
JMXServiceURL url = new JMXServiceURL(SERVICE_1);
System.out.println("url: " + SERVICE_1);
connector = JMXConnectorFactory.connect(url);
MBeanServerConnection connection = connector.getMBeanServerConnection();
System.out.println("Catalina:");
ObjectName objectName = new ObjectName("Catalina:type=Engine");
System.out.println("defaultHost:"
+ (String) connection.getAttribute(objectName,
"defaultHost"));
System.out.println("name:" + (String) connection.getAttribute(objectName, "name"));
System.out.println("baseDir:" + (String) connection.getAttribute(objectName,"baseDir"));
connection.getAttribute(objectName, "valveObjectNames");
objectName = new ObjectName("Catalina:type=Host,host=localhost");
javax.management.ObjectName[] a = (javax.management.ObjectName[]) connection
.getAttribute(objectName, "children");
for (int i = 0; i < a.length; i++) {
System.out.println(a[i].toString());
}
System.out.println("\nMemory");
System.out.println("memory: HeapMemoryUsage");
objectName = new ObjectName("java.lang:type=Memory");
CompositeDataSupport heapMemoryUsage = (CompositeDataSupport) connection.getAttribute(objectName, "HeapMemoryUsage");
System.out.println("committed = " + convertKB(heapMemoryUsage.get("committed")));
System.out.println("init = " + convertKB(heapMemoryUsage.get("init")));
System.out.println("max = " + convertKB(heapMemoryUsage.get("max")));
System.out.println("used = " + convertKB(heapMemoryUsage.get("used")));
System.out.println("\nmemory: NonHeapMemoryUsage");
CompositeDataSupport nonHeapMemoryUsage = (CompositeDataSupport) connection.getAttribute(objectName, "NonHeapMemoryUsage");
System.out.println("committed = " + convertKB(nonHeapMemoryUsage.get("committed")));
System.out.println("init = " + convertKB(nonHeapMemoryUsage.get("init")));
System.out.println("max = " + convertKB(nonHeapMemoryUsage.get("max")));
System.out.println("used = " + convertKB(nonHeapMemoryUsage.get("used")));
System.out.println("\nThread");
objectName = new ObjectName("java.lang:type=Threading");
//线程总数
System.out.println("ThreadCount = " + connection.getAttribute(objectName, "ThreadCount"));
//守护线程
System.out.println("DaemonThreadCount = " + connection.getAttribute(objectName, "DaemonThreadCount"));
//返回自从Java虚拟机启动或峰值重置以来峰值活动线程计数
System.out.println("PeakThreadCount = " + connection.getAttribute(objectName, "PeakThreadCount"));
System.out.println("CurrentThreadCpuTime = " + connection.getAttribute(objectName, "CurrentThreadCpuTime") + "ms");
System.out.println("CurrentThreadUserTime = " + connection.getAttribute(objectName, "CurrentThreadUserTime") + "ms");
System.out.println("\nClassLoading");
objectName = new ObjectName("java.lang:type=ClassLoading");
System.out.println("TotalLoadedClassCount = " + connection.getAttribute(objectName, "TotalLoadedClassCount") + "个");
System.out.println("\nCpu");
objectName = new ObjectName("java.lang:type=OperatingSystem");
long start = System.currentTimeMillis();
long startC = (Long) connection.getAttribute(objectName, "ProcessCpuTime");
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
System.out.println("中断异常");
}
long end = System.currentTimeMillis();
long endC = (Long) connection.getAttribute(objectName, "ProcessCpuTime");
//end - start 即为当前采集的时间单元,单位ms
//endT - startT 为当前时间单元内cpu使用的时间,单位为ns
//所以:
int availableProcessors = (Integer)connection.getAttribute(objectName, "AvailableProcessors");
double ratio = (endC-startC) / 1000000.0 / (end-start) / availableProcessors;
System.out.println("cup使用率 = " + ratio * 100 + "%");
} catch (Exception ex) {
System.out.println(ex);
} finally {
try {
connector.close();
} catch (Exception ex) {
System.out.println(ex);
}
}
}
private static String convertKB(Object obj){
long src = (Long)obj;
if (src <= 0L) {
return "0KB";
}
return (double)src / 1024 + "KB";
}}
执行上面的代码,如果报javax.management.AttributeNotFoundException: Cannot find attribute valveObjectNames for StandardEngine[Catalina]错误,可以将以下的代码注释掉。
同时代码在取CPU使用率时,是有5秒钟的时间间隔,这个是为了取到精确的CPU使用率,要不取到的都是0.
4.总结
以上主是演示了java代码如何去监控Tomcat,其实在其它的应用中,也昌一样的配置,只是要将配置放入到启动的脚本中。其余的都是一样的。最后,笔者要说明的就是,其它在用jmx监控应用时,笔者也是参考了很多人的代码,然后将其总结在此博客,希望对读者有所帮助。