jmx编程监控mysql_JMX-JAVA进程监控利器
Java 管理扩展(Java Management Extension,JMX)是从jdk1.4开始的,但从1.5时才加到jdk里面,并把API放到java.lang.management包里面。
如果一个 Java 对象可以由一个遵循 JMX 规范的管理器应用管理,那么这个Java 对象就可以称为一个可由 JMX 管理的资源。
要使一个 Java 对象可管理,则必须创建相应的 MBean 对象,并通过这些 MBean 对象管理相应的 Java 对象。当拥有 MBean 类后,需要将其实例化并注册到 MBeanServer 上。
一共有四种类型的 MBean , 分别是标准类型 MBean, 动态类型 MBean, 开放类型 MBean 和模型类型 MBean。
注:
一个java进程里面可以有多个不同名字的mBeanServer ,每个mbs都是一个独立的容器,用了管理mbean
每个mbs都可以注册多个rmi port,http port等
platformMBeanServer 是由jvm创建的,并添加了一些系统的mbean,如cpu,内存,网络,线程等等
1、本机使用
当我们启动java进程后,经常会使用jps,jinfo,jmap,jstat等jdk自带的命令去查询进程的状态,这其中的原理就是,当java进程启动后,会创建一个用于本机连接的“localConnectorAddress”放到当前用户目录下,当使用jps等连接时,会到当前用户目录下取到“localConnectorAddress”并连接。
packagecom.dxz.study;importjava.io.IOException;importjava.util.List;importjava.util.Map;importjava.util.Properties;importjava.util.Set;importjavax.management.MBeanServerConnection;importjavax.management.ObjectName;importjavax.management.remote.JMXConnector;importjavax.management.remote.JMXConnectorFactory;importjavax.management.remote.JMXServiceURL;importorg.junit.Test;importcom.sun.tools.attach.VirtualMachine;importcom.sun.tools.attach.VirtualMachineDescriptor;public classJmxTest {
@Testpublic voidtest1() {
List vms =VirtualMachine.list();for(VirtualMachineDescriptor desc : vms) {
VirtualMachine vm;try{
System.out.println("desc:" +desc);
System.out.println("进程id:"+desc.id());
vm=VirtualMachine.attach(desc);
}catch(Exception e) {
e.printStackTrace();continue;
}
JMXConnector connector= null;try{
Properties props=vm.getAgentProperties();for (Map.Entryentry : props.entrySet()) {
System.out.println(entry.getKey()+ "->" +entry.getValue());
}
String connectorAddress= props.getProperty("com.sun.management.jmxremote.localConnectorAddress");if (connectorAddress == null) {
System.out.println("connectorAddress is null");continue;
}
System.out.println("conn:" +connectorAddress);//以下代码用于连接指定的jmx,本地或者远程
JMXServiceURL url = newJMXServiceURL(connectorAddress);//JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/TestJMXServer");
connector =JMXConnectorFactory.connect(url);
MBeanServerConnection mbeanConn=connector.getMBeanServerConnection();
Set beanSet = mbeanConn.queryNames(null, null);//...
} catch(Exception e) {
e.printStackTrace();
}finally{try{if (connector != null) connector.close();break;
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
pom.xml
4.0.0
com.dxz
study
0.0.1-SNAPSHOT
jar
study
http://maven.apache.org
UTF-8
junit
junit
4.1
test
org.glassfish.external
opendmk_jdmkrt_jar
1.0-b01-ea
org.jmockit
jmockit
1.24
上面代码有时候取不到本地连接地址,这个时候需要尝试让agent加载management-agent.jar,完整代码如下:
packagecom.dxz.study;importjava.io.File;importjava.io.IOException;importjava.lang.reflect.Method;importjava.net.URL;importjava.net.URLClassLoader;importjava.util.List;importjava.util.Properties;public classAbstractJmxCommand {private static final String CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";public staticString getJVM() {return System.getProperty("java.vm.specification.vendor");
}public static booleanisSunJVM() {//need to check for Oracle as that is the name for Java7 onwards.
return getJVM().equals("Sun Microsystems Inc.") || getJVM().startsWith("Oracle");
}public static voidmain(String[] args) {if (args == null || args.length == 0) {
System.out.println("Usage: pid");return;
}int pid = Integer.valueOf(args[0]);
System.out.println(newAbstractJmxCommand().findJMXUrlByProcessId(pid));
}/*** Finds the JMX Url for a VM by its process id
*
*@parampid
* The process id value of the VM to search for.
*
*@returnthe JMX Url of the VM with the given pid or null if not found.*/
//@SuppressWarnings({ "rawtypes", "unchecked" })
protected String findJMXUrlByProcessId(intpid) {if(isSunJVM()) {try{//Classes are all dynamically loaded, since they are specific//to Sun VM//if it fails for any reason default jmx url will be used//tools.jar are not always included used by default class//loader, so we//will try to use custom loader that will try to load tools.jar
String javaHome= System.getProperty("java.home");
String tools= javaHome + File.separator + ".." + File.separator + "lib" + File.separator + "tools.jar";
URLClassLoader loader= new URLClassLoader(new URL[] { newFile(tools).toURI().toURL() });
Class virtualMachine= Class.forName("com.sun.tools.attach.VirtualMachine", true, loader);
Class virtualMachineDescriptor= Class.forName("com.sun.tools.attach.VirtualMachineDescriptor", true,
loader);
Method getVMList= virtualMachine.getMethod("list", (Class[]) null);
Method attachToVM= virtualMachine.getMethod("attach", String.class);
Method getAgentProperties= virtualMachine.getMethod("getAgentProperties", (Class[]) null);
Method getVMId= virtualMachineDescriptor.getMethod("id", (Class[]) null);
List allVMs= (List) getVMList.invoke(null, (Object[]) null);for(Object vmInstance : allVMs) {
String id= (String) getVMId.invoke(vmInstance, (Object[]) null);if(id.equals(Integer.toString(pid))) {
Object vm= attachToVM.invoke(null, id);
Properties agentProperties= (Properties) getAgentProperties.invoke(vm, (Object[]) null);
String connectorAddress=agentProperties.getProperty(CONNECTOR_ADDRESS);if (connectorAddress != null) {returnconnectorAddress;
}else{break;
}
}
}//上面的尝试都不成功,则尝试让agent加载management-agent.jar
Method getSystemProperties = virtualMachine.getMethod("getSystemProperties", (Class[]) null);
Method loadAgent= virtualMachine.getMethod("loadAgent", String.class, String.class);
Method detach= virtualMachine.getMethod("detach", (Class[]) null);for(Object vmInstance : allVMs) {
String id= (String) getVMId.invoke(vmInstance, (Object[]) null);if(id.equals(Integer.toString(pid))) {
Object vm= attachToVM.invoke(null, id);
Properties systemProperties= (Properties) getSystemProperties.invoke(vm, (Object[]) null);
String home= systemProperties.getProperty("java.home");//Normally in ${java.home}/jre/lib/management-agent.jar//but might//be in ${java.home}/lib in build environments.
String agent= home + File.separator + "jre" + File.separator + "lib" +File.separator+ "management-agent.jar";
File f= newFile(agent);if (!f.exists()) {
agent= home + File.separator + "lib" + File.separator + "management-agent.jar";
f= newFile(agent);if (!f.exists()) {throw new IOException("Management agent not found");
}
}
agent=f.getCanonicalPath();
loadAgent.invoke(vm, agent,"com.sun.management.jmxremote");
Properties agentProperties= (Properties) getAgentProperties.invoke(vm, (Object[]) null);
String connectorAddress=agentProperties.getProperty(CONNECTOR_ADDRESS);//detach 这个vm
detach.invoke(vm, (Object[]) null);if (connectorAddress != null) {returnconnectorAddress;
}else{break;
}
}
}
}catch(Exception ignore) {
ignore.printStackTrace();
}
}return null;
}
}
2、远程连接
毫无疑问,若想远程连接访问,肯定需要mBeanServer注册一个或多个端口,如rmi端口,http端口等。
2.1 rmi端口注册及访问
有两种方法,一种直接在代码里面指定rmi端口,并绑定,如下,此种方法需要使用客户端连接代码访问,另一种代码不用指定端口,之需把mbean注册到platformMBeanServer 里面,并在启动进程时加jmx参数指定,用这种方法可以通过jconsole,jvisualvm远程访问。
2.1.1 直接在代码里面绑定端口
@Testpublic void testJmxRmiRegist() throwsException {int rmiPort = 2222;
String jmxServerName= "com.dxz.study.TestJmxRmiRegist";//jdkfolder/bin/rmiregistry.exe 9999
Registry registry =LocateRegistry.createRegistry(rmiPort);
MBeanServer mbs=MBeanServerFactory.createMBeanServer(jmxServerName);
System.out.println(mbs);//mbs = MBeanServerFactory.createMBeanServer();//新建MBean ObjectName, 在MBeanServer里标识注册的MBean
ObjectName name = new ObjectName(jmxServerName + ":type=HelloWorld");//HtmlAdaptorServer adapter = new HtmlAdaptorServer();//在MBeanServer里注册MBean, 标识为ObjectName(com.tenpay.jmx:type=Echo)
mbs.registerMBean(newHelloWorld(), name);
JMXServiceURL url= new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + rmiPort + "/" +jmxServerName);
System.out.println("JMXServiceURL: " +url.toString());
JMXConnectorServer jmxConnServer= JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
jmxConnServer.start();
Thread.sleep(1000 * 60 * 10);
}
上面程序是新建了个mbeanserver,并通过rmi绑定到2222端口上,等待客户端连接。
2.1.2 通过jmx参数启动进程
#JVMARGS="$JVMARGS -Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
通过这种把进程的jmx监控绑定指定的端口,即可在远端通过jconsole进行监控。
2.2通过http访问
@Testpublic void testJmxHtmlAdapter() throwsException {
String jmxServerName= "com.dxz.study.TestJmxRmiRegist";//jdkfolder/bin/rmiregistry.exe 9999
MBeanServer mbs=MBeanServerFactory.createMBeanServer(jmxServerName);
System.out.println(mbs);//mbs = MBeanServerFactory.createMBeanServer();//新建MBean ObjectName, 在MBeanServer里标识注册的MBean
ObjectName name = new ObjectName(jmxServerName + ":type=HelloWorld");//HtmlAdaptorServer adapter = new HtmlAdaptorServer();//创建MBean//在MBeanServer里注册MBean, 标识为ObjectName(com.tenpay.jmx:type=Echo)
mbs.registerMBean(newHelloWorld(), name);
HtmlAdaptorServer adapter= newHtmlAdaptorServer();
ObjectName adapterName;
adapterName= new ObjectName(jmxServerName + ":name=" + "htmladapter");
adapter.setPort(8082);
adapter.start();
mbs.registerMBean(adapter, adapterName);
Thread.sleep(1000 * 60 * 10);
}
以上代码用到了HtmlAdaptorServer,
org.glassfish.external
opendmk_jdmkrt_jar
1.0-b01-ea
然后用浏览器访问即可
3、客户端连接
packagecom.dxz.study;importjava.util.Set;importjavax.management.MBeanServerConnection;importjavax.management.ObjectName;importjavax.management.remote.JMXConnector;importjavax.management.remote.JMXConnectorFactory;importjavax.management.remote.JMXServiceURL;importorg.junit.Test;public classJmxClientTest {
@Testpublic voidtest1() {try{
JMXServiceURL url= new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:2222/com.dxz.study.TestJmxRmiRegist");
JMXConnector connector=JMXConnectorFactory.connect(url);
MBeanServerConnection mbeanConn=connector.getMBeanServerConnection();
Set beanSet = mbeanConn.queryNames(null, null);
System.out.println(beanSet);
}catch(Exception e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:
[com.dxz.study.TestJmxRmiRegist:type=HelloWorld, JMImplementation:type=MBeanServerDelegate]
4、jconsole连接(待验证)
5、java进程自带的mbean
当我们在用jconsole、jvisualvm进行监控java进程时,通常都能看到cpu、内存、线程、垃圾收集等使用情况,其实数据都是通过jmx从jvm提供的一些mbean里面取的。主要如下:
ClassLoadingMXBean
ClassLoadMXBean 包括一些类的装载信息,比如有多少类已经装载 / 卸载(unloaded),虚拟机类装载的 verbose 选项(即命令行中的 Java – verbose:class 选项)是否打开,还可以帮助用户打开 / 关闭该选项。
CompilationMXBean
CompilationMXBean 帮助用户了解当前的编译器和编译情况,该 mxbean 提供的信息不多。
GarbageCollectorMXBean
相对于开放人员对 GC 的关注程度来说,该 mxbean 提供的信息十分有限,仅仅提供了 GC 的次数和 GC 花费总时间的近似值。但是这个包中还提供了三个的内存管理检测类:MemoryManagerMXBean,MemoryMXBean 和 MemoryPoolMXBean。
MemoryManagerMXBean
这个类相对简单,提供了内存管理类和内存池(memory pool)的名字信息。
MemoryMXBean
这个类提供了整个虚拟机中内存的使用情况,包括 Java 堆(heap)和非 Java 堆所占用的内存,提供当前等待 finalize 的对象数量,它甚至可以做 gc(实际上是调用 System.gc)。
MemoryPoolMXBean
该信息提供了大量的信息。在 JVM 中,可能有几个内存池,因此有对应的内存池信息,因此,在工厂类中,getMemoryPoolMXBean() 得到是一个 MemoryPoolMXBean 的 list。每一个 MemoryPoolMXBean 都包含了该内存池的详细信息,如是否可用、当前已使用内存 / 最大使用内存值、以及设置最大内存值等等。
OperatingSystemMXBean
该类提供的是操作系统的简单信息,如构架名称、当前 CPU 数、最近系统负载等。
RuntimeMXBean
运行时信息包括当前虚拟机的名称、提供商、版本号,以及 classpath、bootclasspath 和系统参数等等。
ThreadMXBean
在 Java 这个多线程的系统中,对线程的监控是相当重要的。ThreadMXBean 就是起到这个作用。ThreadMXBean 可以提供的信息包括各个线程的各种状态,CPU 占用情况,以及整个系统中的线程状况。从 ThreadMXBean 可以得到某一个线程的 ThreadInfo 对象。这个对象中则包含了这个线程的所有信息。
要获得这些信息,我们首先通过 java.lang.management.ManagementFactory这个工厂类来获得一系列的 MXBean。
ClassLoadingMXBean mbs =ManagementFactory.getClassLoadingMXBean();
System.out.println("loadedClass:" + mbs.getLoadedClassCount());