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