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