JMX相关笔记

admin 2023年8月11日01:05:21评论14 views字数 7667阅读25分33秒阅读模式

JMX简介

JMX是Java Management Extensions(Java管理扩展)的缩写,是一个为应用程序植入管理功能的框架。

JMX理解

这么打眼一看的话,不是太好理解,所以贴上如下这张图。

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; }
@Override public String getName() { return name; }
@Override public void setName(String name) { this.name = name; }
@Override public int getAge() { return age; }
@Override public void setAge(int age) { this.age = age; }
@Override 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端:可以看到这里会产生一个地址,这个地址就是客户端需要连接的地址。

JMX相关笔记

创建客户端:这里的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(); }}

可以看到在客户端连接并调用方法之后,在服务端输出。可以看到已成功调用

JMX相关笔记

创建MbeanServer的另一种方式:

MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();

这两种创建MbeanServer的区别就是如上这一种在jsconsole中是可以看到方法并且修改调用的,第一种方式是不行的。

我们修改成第二种方式,然后使用jconsol连接我们的JMXServer。

点击连接使用不安全连接。

JMX相关笔记

然后点击Mbean:这里我们可以将Tom值改成其他的点击刷新。

JMX相关笔记

然后点击操作中的study点击调用。

JMX相关笔记

可以发现成功调用:

JMX相关笔记

但是如果我们使用第一种方式创建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。

最终创建的对象如下:

JMX相关笔记

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);

这个名字是根据官方的注释来起的,如果你起的其他的也是没有任何问题的。

JMX相关笔记

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);

JMX相关笔记

这里可以通过invoke方法调用,这里第一个参数就是MBean的唯一标识,第二个就是方法名,然后方法的参数值。

beanServerConnection.invoke(objectName, "study", new Object[]{"aaa"}, new String[]{String.class.getName()});

JMX相关笔记

如上就是JXM相关的基础操作了。

原文始发于微信公众号(Relay学安全):JMX相关笔记

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年8月11日01:05:21
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JMX相关笔记https://cn-sec.com/archives/1948094.html

发表评论

匿名网友 填写信息