Java 之 管理扩展JMX

1.概念:

  a.定义:JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。

  b.作用:

    1)程序初哥一般是写死在程序中,到要改变的时候就去修改代码,然后重新编译发布。

    2)程序熟手则配置在文件中(JAVA一般都是properties文件),到要改变的时候只要修改配置文件,但还是必须重启系统,以便读取配置文件里最新的值。

    3)程序好手则会写一段代码,把配置值缓存起来,系统在获取的时候,先看看配置文件有没有改动,如有改动则重新从配置里读取,否则从缓存里读取。

    4)程序高手则懂得物为我所用,用JMX把需要配置的属性集中在一个类中,然后写一个MBean,再进行相关配置。另外JMX还提供了一个工具页,以方便我们对参数值进行修改。

 

 

 

2.Mbean准备

  a.创建供外部调用的接口

public interface HelloMBean {

    public String getName();

    public void setName(String name);

    public int getAge();

    public void setAge(int age);

    public void helloWorld(String word);

}

    注意:接口 和 实现类 命名必须遵循一定的规范,如果我们的 MBean(实现类) 为 Hello,则接口必须为Hello + MBean

 

  b.创建实现类MBean

public class Hello implements HelloMBean {

    @Override
    public String getName() {
        System.out.println("获取名称:" + MyMemory.getInstance().getName());
        return MyMemory.getInstance().getName();
    }

    @Override
    public void setName(String name) {
        System.out.println("设置名称:" + name);
        MyMemory.getInstance().setName(name);
    }

    @Override
    public int getAge() {
        System.out.println("获取年龄:" + MyMemory.getInstance().getAge());
        return MyMemory.getInstance().getAge();
    }

    @Override
    public void setAge(int age) {
        System.out.println("设置年龄:" + age);
        MyMemory.getInstance().setAge(age);
    }

    @Override
    public void helloWorld(String word) {
        System.out.println("Hello world:" + word);
    }
}

 

  c.创建内存类用于测试

public class MyMemory {

    //单例
    private static MyMemory instance = new MyMemory();
    private MyMemory(){}
    public static MyMemory getInstance(){
        return instance;
    }

    private String name;
    private int 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;
    }
}

 

 

 

3.开启远程JMX服务

  a.开启方式一:通过Java启动命令开启

    1)注册MBean

    public static void main( String[] args ) throws Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName helloName = new ObjectName("myJmxBean:name=helloService");
        server.registerMBean(new Hello(), helloName);
        Thread.sleep(60 * 60 * 1000);
    }

       注意:ObjectName中的取名是有一定规范的,格式为:“域名:name=MBean名称”,其中域名和MBean的名称可以任意取。这样定义后,就可以唯一标识我们定义的这个MBean的实现类了

 

    2)在java启动参数中添加

#相关 JMX 代理侦听开关
-Dcom.sun.management.jmxremote=true 
#相关 JMX 代理侦听请求的端口
-Dcom.sun.management.jmxremote.port=9999 
#指定是否使用 SSL 通讯
-Dcom.sun.management.jmxremote.ssl=false 
#指定是否需要密码验证
-Dcom.sun.management.jmxremote.authenticate=false 
#服务器端的IP
-Djava.rmi.server.hostname=192.168.0.141

    idea在此处添加

 

 

 

  b.开启方式二:通过java代码启动

    1)注册并启动jmx

    public static void main( String[] args ) throws Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName helloName = new ObjectName("myJmxBean:name=helloService");
        server.registerMBean(new Hello(), helloName);

        //这个步骤很重要,注册一个端口,绑定url后用于客户端通过rmi方式连接JMXConnectorServer
        LocateRegistry.createRegistry(9999);
        //URL路径的结尾可以随意指定,但如果需要用Jconsole来进行连接,则必须使用jmxrmi
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
        JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
        jcs.start();
        System.out.println("rmi start");
    }

 

 

 

4.连接JMX服务

  a.连接方式一:使用JConsole连接

 

 

 

 

  b.连接方式二:使用Java客户端访问

public class JmxClient {

    public static void main(String[] args) throws Exception {
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
        JMXConnector connect = JMXConnectorFactory.connect(jmxServiceURL, null);
        MBeanServerConnection connection = connect.getMBeanServerConnection();

//        String[] domains = connection.getDomains();
//        for (int i = 0; i < domains.length; i++) {
//            System.out.println("[domian]" + i + "=" + domains[i]);
//        }

        // 注册名和之前server的一致
        ObjectName objectName = new ObjectName("myJmxBean:name=helloService");
        // 获取参数
        System.out.println("--------------------- get params ---------------------");
        getParam(connection, objectName);
        // 更改参数
        System.out.println("--------------------- set params ---------------------");
        changeParams(connection, objectName);
        getParam(connection, objectName);
        //通用方法
        System.out.println("--------------------- use method ---------------------");
        useMethod(connection, objectName);
        getParam(connection, objectName);
    }

    /**
     * 对method的调用, 采用反射的方式进行
     */
    public static void useMethod(MBeanServerConnection connection, ObjectName objectName) {
        HelloMBean helloMBean = MBeanServerInvocationHandler.newProxyInstance(connection, objectName, HelloMBean.class, true);
        helloMBean.setAge(29);
        helloMBean.helloWorld("myWorld");
    }

    /**
     * 可进行相关参数修改
     * 通过setAttribute、getAttrubute方法来进行操作,则属性的首字母要大写
     */
    public static void changeParams(MBeanServerConnection connection, ObjectName objectName) throws Exception {
        connection.setAttribute(objectName, new Attribute("Name", "vettel"));
        connection.setAttribute(objectName, new Attribute("Age", 28));
    }

    /**
     * 获取参数
     */
    public static void getParam(MBeanServerConnection connection, ObjectName objectName) throws Exception {
        int age = (int) connection.getAttribute(objectName, "Age");
        String name = (String) connection.getAttribute(objectName, "Name");
        System.out.println("name: " + name + ", age: " + age);
    }

}

 

 

 

5.权限控制方式一:启动命令中开启

  a.修改java启动参数

#开启权限验证
-Dcom.sun.management.jmxremote.authenticate=true
#用户权限配置文件位置
-Dcom.sun.management.jmxremote.access.file=E:\TestSpace\test-jmx\config\jmx.access
#用户密码
-Dcom.sun.management.jmxremote.password.file=E:\TestSpace\test-jmx\config\jmx.password

 

  b.创建 jmx.access 文件

monitor readonly
admin readwrite

    注:格式为 [用户名] [权限]

 

  c.创建 jmx.password 文件

monitor m123
admin a123

    注:格式为 [用户名] [密码]

 

  d.Windows下由于权限问题,需要修改 jmx.access 和 jmx.password 文件权限

    1)右键 -> 属性 -> 安全 -> 高级 

    2)点击“禁用继承”,添加当前用户,删除其他用户及用户组

 

 

 

6.权限控制方式一:java代码启动中开启

   a.服务端

        //------------------------权限设置------------------------
        Map<String, Object> environment = new HashMap<>();
        environment.put(JMXConnectorServer.AUTHENTICATOR, new JMXAuthenticator() {
            public Subject authenticate(Object credentials) {
                if(credentials == null){
                    throw new SecurityException("Authentication failed! ");
                }
                String[] sCredentials = (String[]) credentials;
                String userName = sCredentials[0];
                String password = sCredentials[1];
                if ("admin".equals(userName) && "a123".equals(password)) {
                    Set principals = new HashSet();
                    principals.add(new JMXPrincipal(userName));
                    return new Subject(true, principals, Collections.EMPTY_SET, Collections.EMPTY_SET);
                }
                throw new SecurityException("Authentication failed! ");
            }
        });
        //------------------------------------------------

        JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, environment, server);

 

  b.客户端

        //------------------------账号密码登录------------------------
        Map<String, Object> environment = new HashMap<>();
        environment.put(JMXConnector.CREDENTIALS, new String[] { "admin", "a123" });
        //------------------------------------------------

        JMXConnector connect = JMXConnectorFactory.connect(jmxServiceURL, environment);

 

 

 

7.参考文档:

  https://www.cnblogs.com/wenbronk/p/8535103.html

  https://www.cnblogs.com/dongguacai/p/5900507.html

  https://www.cnblogs.com/FlyAway2013/p/jmx.html

 

posted @ 2019-11-07 15:39  晨M风  阅读(510)  评论(0编辑  收藏  举报