JMX简介
JMX是Java Management Extensions(Java管理扩展)的缩写,是一个为应用程序植入管理功能的框架。
JMX理解
这么打眼一看的话,不是太好理解,所以贴上如下这张图。
在JMX中包含了如上这几个角色:
MBean,MBeanServer,Connector。后面会将这几个关键词一一介绍。
那么现在去尝试理解这张图:
首先当我们有两个虚拟机的时候,左边是第一个JVM,右边是第二个JVM,如果这两个JVM想通过JVM进行一个沟通的话,首先第一个JVM中有一个ManagedApplication表示这个程序是被管理的,在这里面有一个Mbean,Mbean表示的就是有一定的资源可以被管理,比如相关的一些方法,那么Mbean是注册在MbeanServer中的,那么MbeanServer为了将自己的服务向外提供,那么就在出口这块开启了一个Connector Server,Connector Server会监听外部的请求。
那么现在来到我们第二个JVM当中,在这个JVM中的JMX Connector是一个Connector Client,也就是客户端,那么现在就清楚了左边的是服务端,右边的是客户端。那么现在就通过Connector Client来连接到Connector Server,然后通过MbeanServer找到Mbean这个资源,然后调用Mbean当中的一些方法。
那么总的来说第一个JVM作为一个被动者,而第二个JVM作为一个主动者,主动者去连接被动者获取资源。
说了这么多我们用代码来体会一下:
代码实例
首先创建Mbean接口:
public interface UserMBean {
String getName();
void setName(String name);
int getAge();
void setAge(int age);
void study(String subject);
}
然后创建他的实现类: 注意这里创建Mbean实现类的时候需要符号规范,就比如说你的接口是UserMBean,那么你的实现类就是User,不能是UserImpl或者其他类名。
public class User implements UserMBean{
private String name;
private int age;
public UserImpl(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void study(String subject) {
String message = String.format("%s (%d) is studying %s.", name, age, subject);
System.out.println(message);
}
}
创建JXM服务端:
这里代码就是首先创建MBeanServer,让将MBean注册进MBeanServer中,然后创建Connector Server,放出一个监听的地址,方便客户端通过这个地址进行连接,最后开启监听,等待客户端的连接。
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
public class JMXServer {
public static void main(String[] args) throws Exception{
//创建MbeanSeerver
MBeanServer beanServer = MBeanServerFactory.createMBeanServer();
//将Mbean注册进去
UserImpl bean = new UserImpl("job",20);
ObjectName objectName = new ObjectName("com.relaysec.bean:type=user,name=UserImpl");
beanServer.registerMBean(bean,objectName);
//创建Connector Server
JMXServiceURL serviceURL = new JMXServiceURL("rmi","127.0.0.1",9876);
JMXConnectorServer connectorServer =
JMXConnectorServerFactory.newJMXConnectorServer(serviceURL,null,beanServer);
//开启监听
connectorServer.start();
JMXServiceURL connnectorServerAddress = connectorServer.getAddress();
System.out.println(connnectorServerAddress);
Thread.sleep(5000);
connectorServer.stop();
}
}
运行Server端:可以看到这里会产生一个地址,这个地址就是客户端需要连接的地址。
创建客户端:这里的connectorAddress地址就是服务端输出的那个地址。
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class JMXClient {
public static void main(String[] args) throws Exception{
// 第一步,创建 Connector Client
String connectorAddress = "service:jmx:rmi://127.0.0.1:9876/stub/rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LnJlbW90ZS5ybWkuUk1JU2VydmVySW1wbF9TdHViAAAAAAAAAAICAAB4cgAaamF2YS5ybWkuc2VydmVyLlJlbW90ZVN0dWLp/tzJi+FlGgIAAHhyABxqYXZhLnJtaS5zZXJ2ZXIuUmVtb3RlT2JqZWN002G0kQxhMx4DAAB4cHc0AAtVbmljYXN0UmVmMgAACTEyNy4wLjAuMQAAJpQZYtuDeP/Pn7mEUoUAAAGJ3q2NmIABAHg=";
JMXServiceURL address = new JMXServiceURL(connectorAddress);
JMXConnector connector = JMXConnectorFactory.connect(address);
// 第二步,获取 MBeanServerConnection 对象
MBeanServerConnection beanServerConnection = connector.getMBeanServerConnection();
// 第三步,向 MBean Server 发送请求
ObjectName objectName = new ObjectName("com.relaysec.bean:type=user,name=UserImpl");
String[] array = new String[]{"Chinese", "Math", "English"};
for (String item : array) {
beanServerConnection.invoke(objectName, "study", new Object[]{item}, new String[]{String.class.getName()});
}
// 第四步,关闭 Connector Client
connector.close();
}
}
可以看到在客户端连接并调用方法之后,在服务端输出。可以看到已成功调用
创建MbeanServer的另一种方式:
MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
这两种创建MbeanServer的区别就是如上这一种在jsconsole中是可以看到方法并且修改调用的,第一种方式是不行的。
我们修改成第二种方式,然后使用jconsol连接我们的JMXServer。
点击连接使用不安全连接。
然后点击Mbean:这里我们可以将Tom值改成其他的点击刷新。
然后点击操作中的study点击调用。
可以发现成功调用:
但是如果我们使用第一种方式创建MBeanServer,他是没有我们这个Mbean这个类的。
好了上面一个小实例相信你对JMX有了一些了解了,那么接下来就来看看具体的类中的结构都是怎么样的。
MBean
在了解MBean之前我们需要去了解Resource这个概念,Resource他所代表的就是资源,这里的资源可以分为很多种,可能是应用层面,比如说用户的数量,也可能在Class层面,比如说类中的某一个字段记录了某个方法调用的次数。
JMX 的一个主要目标就是对 resource(资源)进行 management(管理)和 monitor(监控),它要把一个我们关心的事物(resource)给转换成 manageable resource。
接下来我们来说MBean的概念,MBean 是 managed bean 的缩写,它就代表 manageable resource 本身,或者是对 manageable resource 的进一步封装, 它就是 manageable resource 在 JMX 架构当中所对应的一个“术语”或标准化之后的“概念”。
在JMX当中,MBean有不同的的类型。
-
Standard MBean
-
Dynamic MBean
-
Open MBean
-
Model MBean
这里我们只需要关注Standard MBean即可,这个Standard MBean在书写上有些规范。
1.类名层面,例如有一个User类,这个User类就是我们的资源,他必须实现一个接口,接口的名字必须是UserMBean,也就是说这两个是对应的,你需要在User后面加一个MBean,这就是接口的名字。
2.User类必须拥有一个空参构造器。
3.方法层面,getter方法不能接受参数,比如说int getAge()这样是可以的,但是int getAge(String name)这样是不行的,而setter方法只能接收一个参数,我们可以想到他其实跟我们正常的JavaBean是差不多的。
MBeanServer
在MBean创建完成之后,需要将MBean注册到MBeanServer中,方便客户端去取。
MBeanServer是一个接口,需要通过MBeanServerFactory工厂类进行创建对象。
最终创建的对象是JmxMBeanServer。
可以通过如下两种方式进行创建:
MBeanServer beanServer = MBeanServerFactory.createMBeanServer();
MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
那么这两种方式的不同之处在于第一种创建的MBeanServer,在使用jconsole连接的时候,他不会显示方法。但是使用第二种的话是可以显示方法并且调用的。所以推荐使用第二种方式创建MBeanServer。
最终创建的对象如下:
MBean对象注册到MBeanServer
注册MBean对象到MBeanServer中使用的是registerMBean这个方法,这个方法接收一个Object类型和一个ObjectName的一个类型,那么他的第一个参数就是MBean对象,第二个参数就是MBeanName值。这个ObjectName是需要指定唯一的。
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException;
对应如下代码,这里的ObjectName对应的就是这个bean的唯一标识,他是一个key=>value的一个形式,前面的是一个包名相当于key,value就是type=user,name=UserImpl,当然你也可以起别的名字,也是没有任何问题的。
User bean = new User("job",20);
ObjectName objectName = new ObjectName("com.relaysec.bean:type=user,name=UserImpl");
beanServer.registerMBean(bean,objectName);
这个名字是根据官方的注释来起的,如果你起的其他的也是没有任何问题的。
Connector
Connector Server
Connector Server和MBeanServer这两个是联系在一块的,Connector Server可以建立并发的链接,他是多线程的,也就是说多个客户端可以连接同一个Connector Server。
客户端如果想和Connector Serve来建立连接,需要一个地址来建立连接。
创建一个JMXConnectorServer对象需要通过JMXConnectorServerFactory工厂来进行创建,这里需要三个值,第一个值需要传进去一个JMXServiceURL对象,这个对象中的值表示的是rmi协议,ip和端口,第三个参数是就是我们的MBeanServer。
JMXServiceURL serviceURL = new JMXServiceURL("rmi","127.0.0.1",9876);
JMXConnectorServer connectorServer =
JMXConnectorServerFactory.newJMXConnectorServer(serviceURL,null,beanServer);
在创建完JMXConnectorServer对象之后,他是处于一个没有开启的一个状态的。
需要调用start方法对他进行开启监听,开启监听之后,客户端才可以通过地址进行连接。
connectorServer.start();
在开启监听之后,我们就可以通过调用它的getAddress方法来获取到connector server的服务地址,拿到这个地址之后客户端就可以通过这个地址进行连接。
JMXServiceURL connnectorServerAddress = connectorServer.getAddress();
最后需要关闭监听使用stop方法。
connectorServer.stop();
Connector Client
服务端已经在监听了,那么现在客户端可以通过服务端提供的地址来进行连接。
String connectorAddress = "service:jmx:rmi://127.0.0.1:9876/stub/rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LnJlbW90ZS5ybWkuUk1JU2VydmVySW1wbF9TdHViAAAAAAAAAAICAAB4cgAaamF2YS5ybWkuc2VydmVyLlJlbW90ZVN0dWLp/tzJi+FlGgIAAHhyABxqYXZhLnJtaS5zZXJ2ZXIuUmVtb3RlT2JqZWN002G0kQxhMx4DAAB4cHc0AAtVbmljYXN0UmVmMgAACTEyNy4wLjAuMQAAJpRxNJPhlOHTJo9l1lMAAAGJ3q9SGYABAHg=";
JMXServiceURL address = new JMXServiceURL(connectorAddress);
JMXConnector connector = JMXConnectorFactory.connect(address);
连接成功之后我们可以通过调用getMBeanServerConnection方法来获取一个MBeanServerConnection对象,然后我们就可以通过MBeanServerConnection对象来和MBeanServer进行交互了,拿到MBeanServer之后,因为Mbean就注册在MBeanServer中,所以我们可以去调用MBean的相关方法。
MBeanServerConnection beanServerConnection = connector.getMBeanServerConnection();
ObjectName objectName = new ObjectName("com.nanchensec.relaysec:type=aa");
MBeanInfo mBeanInfo = beanServerConnection.getMBeanInfo(objectName);
这里可以通过invoke方法调用,这里第一个参数就是MBean的唯一标识,第二个就是方法名,然后方法的参数值。
beanServerConnection.invoke(objectName, "study", new Object[]{"aaa"}, new String[]{String.class.getName()});
如上就是JXM相关的基础操作了。
原文始发于微信公众号(Relay学安全):JMX相关笔记
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论