您的位置:首页 > 其它

jmx介绍及示例

2015-11-12 14:01 363 查看
最近项目中要用到JMX,好好研究了一些,发现网上也有很多没有解释清楚的,自己动手试过后总结一下。

一、什么是jmx

JMX (Java Management Extensions)是一个为应用程序,设备,系统等植入管理功能的框架。JMX是一个规范,jdk5之前有很多实现,JDK5.0则内嵌了进来,安装JDK5.0就可以开发基于JMX的代码了。简而言之,使用jmx我们可以监控jvm的运行情况,并且可扩展我们自己想要添加的监控逻辑。

二、jmx架构



Probe Level(设备层)负责资源的检测(获取信息),包含MBeans,通常也叫做Instrumentation Level。

The Agent Level 或者叫做MBean Server,是JMX的核心,连接这个Mbeans和应用程序。

Remote Management Level通过connectors和adaptors来远程操作MBeanServer, Applications可以是大家比较熟悉的控制台,例如JConsole。

三、MBean(Managed beans)

一个MBean是一个被管理的java对象,有点类似于Javabean,一个设备、一个应用或者任何资源都可以被表示为MBean。MBean会暴露一个对外接口,通过这个接口可以读取或者写入一些对象中的属性,操作对象的方法。JMX提供了四种MBean,分别是标准MBean、动态MBean、模型MBean和开发MBean。

(1)标准MBean

Standard Mbean是最简单的MBean,他能管理的资源必须定义在接口中,然后MBean必须实现这个接口,命名必须遵守一定的规范。

示例:

1、编写MBean的接口:ControllerMBean.java

public interface ControllerMBean {
/*需遵从的规范:
1、名字必须以MBean结尾
2、必须有相关属性的getXXX()和setXXX()方法名*/

public void setName(String name);
public String getName();
public String status();
public void start();
public void stop();
}


2、编写MBean接口的实现类:Controller.java

public class Controller implements ControllerMBean {
/*需遵从的规范:
1、名字必须和接口中 "MBean" 的前缀相同(不知道是不是新版本的规定,很多都没有说明,期初没有相同调试程序一直不过)
2、实现接口中的方法*/
private String name;

@Override
public void setName(String name) {
// TODO Auto-generated method stub
this.name=name;
}

@Override
public String getName() {
// TODO Auto-generated method stub
return this.name;
}

@Override
public String status() {
// TODO Auto-generated method stub
System.out.println("this is a Controller MBean,name is " + this.name);
return "this is a Controller MBean,name is " + this.name;   }

@Override
public void start() {
// TODO Auto-generated method stub

}

@Override
public void stop() {
// TODO Auto-generated method stub

}


3、创建一个Agent类:ControllerTestJMX.java

public class ControllerTestJMX {
public static void main(String[] args) throws MalformedObjectNameException, NullPointerException, InstanceAlreadyExistsException, NotCompliantMBeanException, InstanceNotFoundException, ReflectionException, MBeanException
{
//      MBeanServer mbs = MBeanServerFactory.createMBeanServer();//不能在jconsole中使用
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();//可在jconsole中使用

//创建标准MBean
Controller controller = new Controller();
ObjectName controllerName = new ObjectName("myBean:name=controller,type=test");
//将标准MBean注册到MBeanServer中
mbs.registerMBean(controller, controllerName);
mbs.invoke(controllerName, "status", null, null);
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}


添加后可以打开JConsole查看:



(2)动态MBean

动态MBean的最大的特点是可以在运行期暴露自己的管理接口,因此具有更好的灵活性。它的实现是通过实现一个特定的接口DynamicMBean。

当DynamicMBean在JMX agent中注册后,DynamicMBean其实就和StandardMBean 是一样的,外界将首先通过getMBeanInfo获得一个管理接口,其中包含attributes 和 operations 名称,然后才将调用DynamicMBean的getters, setters 和invoke方法。 适合管理那些已经存在的资源和代码。

1、 TestDynamicMBean.java:实现了DynamicMBean接口

public class TestDynamicMBean implements DynamicMBean {
private String mc = "myDynamicMBean";
private MBeanAttributeInfo[] dAttributes;
private MBeanOperationInfo[] opers=new MBeanOperationInfo[3];
private MBeanConstructorInfo[] constructors = new MBeanConstructorInfo[1];
MBeanInfo info;

private String value="hello";
public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

public String getMc() {
return mc;
}

public void setMc(String name) {
this.mc = name;
}

public void printName()
{
System.out.println(mc);
}

public TestDynamicMBean()
{
init();
System.out.println("构造函数");
}
public void init()
{
Constructor[] thisconstructors = this.getClass().getConstructors();
constructors[0] = new MBeanConstructorInfo("TestDynamicMBean(): Constructs a TestDynamicMBean object", thisconstructors[0]);
//可用的方法是在 MBeanInfo 接口中指定的
dAttributes =  new   MBeanAttributeInfo[] {
new   MBeanAttributeInfo( "mc" ,  "String" ,  "缓存名称" ,  true ,   true ,  false )};
/* MBeanOperationInfo opers[] = {
new   MBeanOperationInfo( "printName" , "print" , null , "void" ,MBeanOperationInfo.ACTION),
new   MBeanOperationInfo( "setMc" , "setMc" , null , "void" ,MBeanOperationInfo.ACTION),
new   MBeanOperationInfo( "addAttribute" , "addAttribute" , null , "void" ,MBeanOperationInfo.ACTION)

};*/
opers[0]= new   MBeanOperationInfo( "printName" , "print" , null , "void" ,MBeanOperationInfo.ACTION);
opers[1]= new   MBeanOperationInfo( "setMc" , "setMc" , null , "void" ,MBeanOperationInfo.ACTION);
opers[2]= new   MBeanOperationInfo( "addAttribute" , "addAttribute" , null , "void" ,MBeanOperationInfo.ACTION);

info =  new   MBeanInfo( this .getClass().getName(),
"TestDynamic" ,
dAttributes,
null ,
opers,
null );
}
/*动态添加属性
*
*/

public void addAttribute()
{
System.out.println("开始调用addAttribute");

dAttributes=new MBeanAttributeInfo[2];
dAttributes[0]=new   MBeanAttributeInfo( "name" ,  "String" ,  "缓存name" ,  true ,   true ,  false );
dAttributes[1]=new   MBeanAttributeInfo( "value" ,  "String" ,  "缓存value" ,  true ,   true ,  false );
System.out.println("已增加value属性");
info =  new   MBeanInfo( this .getClass().getName(),
"TestDynamic" ,
dAttributes,
null ,
opers,
null );
}

@Override
public Object getAttribute(String arg0) throws AttributeNotFoundException,
MBeanException, ReflectionException {
// TODO Auto-generated method stub
System.out.println("getAttribute");

if (arg0== null ){
throw   new   AttributeNotFoundException();
}
if ( "name" .equalsIgnoreCase(arg0)){
return   getMc();
}
if ( "value" .equalsIgnoreCase(arg0)){
return   getValue();
}
throw   new   AttributeNotFoundException();
}

@Override
public AttributeList getAttributes(String[] arg0) {
// TODO Auto-generated method stub
return null;
}

@Override
public MBeanInfo getMBeanInfo() {
// TODO Auto-generated method stub
System.out.println("调用getMBeanInfo方法");
System.out.println(dAttributes.length);
return   info;
}

@Override
public Object invoke(String arg0, Object[] arg1, String[] arg2)
throws MBeanException, ReflectionException {
// TODO Auto-generated method stub
if ( "printName" .equals(arg0)){
printName();
}
if ( "addAttribute" .equals(arg0)){
addAttribute();
}
return   null ;
}

@Override
public void setAttribute(Attribute arg0) throws AttributeNotFoundException,
InvalidAttributeValueException, MBeanException, ReflectionException {
// TODO Auto-generated method stub
System.out.println("setAttribute");
String name = arg0.getName();
Object value = arg0.getValue();
if ( "name" .equalsIgnoreCase(name)){
this .setMc(String.valueOf(value));
return ;
}
if ( "value" .equalsIgnoreCase(name)){
this .setValue(String.valueOf(value));
return ;
}
throw   new   AttributeNotFoundException();
}

@Override
public AttributeList setAttributes(AttributeList arg0) {
// TODO Auto-generated method stub
return null;
}


2、Agent类:ControllerTestJMX.java

public class ControllerTestJMX {
public static void main(String[] args) throws MalformedObjectNameException, NullPointerException, InstanceAlreadyExistsException, NotCompliantMBeanException, InstanceNotFoundException, ReflectionException, MBeanException
{
//      MBeanServer mbs = MBeanServerFactory.createMBeanServer();//不能在jconsole中使用
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();//可在jconsole中使用

//创建动态MBean
TestDynamicMBean dynamicMBean = new TestDynamicMBean();
ObjectName dynamicMBeanName = new ObjectName("myDynamicMBean:name=TestDynamicMBean,type=testDynamic");

//将动态MBean注册到MBeanServer中
mbs.registerMBean(dynamicMBean, dynamicMBeanName);
// mbs.invoke(dynamicMBeanName, "status", null, null);

try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}


打开jconsole后进入MBean中会看到刚才注册的myDynamicMBean,点开节点,到操作,点击addAttribute方法(添加属性),提示成功添加属性。再次新打开一个jconsole窗口,可看到新添加的属性value。



四、监控

上面我们已经学习到如何开发jmx用例,如何获得监控的数据是我们的重点,下面通过监控tomcat来描述,tomcat实现了jmx,并且已注册了多种MBean供开发人员监控其性能。



下面实现对tomcat的大部分参数的监控:

1、首先配置tomcat,在catalina.sh中配置JAVA_OPTS

JAVA_OPTS=”-Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=10.25.4.129

-Dcom.sun.management.jmxremote.port=8050 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false”

启动tomcat。其中8050就是jmx远程连接端口(可以修改为其它)

2、编写测试类

public class JmxTest {

public static  void main(String[] args){
try {

String jmxURL = "service:jmx:rmi:///jndi/rmi://10.25.4.129:8050/jmxrmi";//tomcat jmx url
JMXServiceURL serviceURL = new JMXServiceURL(jmxURL);

/*下面这三行代码提供注册所需的用户名,密码,tomcat中参数-Dcom.sun.management.jmxremote.authenticate="false"  配为false,就不需要了*/
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:name=\"http-bio-9666\",type=ThreadPool");
/*其中Catalina:name="http-bio-9666",type=ThreadPool是tomcat中已有的MBean*/
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("连续工作时间:" + JmxTest.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,*");
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();
}
}


关于ObjectName 可以正则匹配,这个一直没有实现,希望有成功经验的可以不吝赐教。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jmx