链接:https://www.zhihu.com/question/36688387/answer/68667704
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
JMX是一种JAVA的正式规范,它主要目的是让程序有被管理的功能。
那么怎么理解所谓的“被管理”呢?试想你开发了一个软件(如WEB网站),它是在24小时不简断运行的,那么你可能会想要“监控”这个软件的运行情况,比如收到了多少数据,有多少人登录等等。或者你又想“配置”这个软件,比如现在访问人数比较多,你想把数据连接池设置得大一些;每天的UV、PV是多少;又或者在业务高峰的期间,你想对接口进行限流,就必须去修改接口并发的配置值。
应用场景:中间件软件WebLogic的管理页面就是基于JMX开发的,而JBoss则整个系统都基于JMX构架。
对于一些参数的修改,网上有一段描述还是比较形象的:
1、程序初哥一般是写死在程序中,到要改变的时候就去修改代码,然后重新编译发布。
2、程序熟手则配置在文件中(JAVA一般都是properties文件),到要改变的时候只要修改配置文件,但还是必须重启系统,以便读取配置文件里最新的值。
3、程序好手则会写一段代码,把配置值缓存起来,系统在获取的时候,先看看配置文件有没有改动,如有改动则重新从配置里读取,否则从缓存里读取。
4、程序高手则懂得物为我所用,用JMX把需要配置的属性集中在一个类中,然后写一个MBean,再进行相关配置。另外JMX还提供了一个工具页,以方便我们对参数值进行修改。
让开发者和管理者可以获取程序运行的状态以及动态的修改程序的相关配置。
SUN依据这个规范在JDK提供了JMX接口,而根据这个接口的实现则有很多种,比如Weblogic的JMX实现、MX4J、JBoss的JMX实现。
我们经常使用的JDK中SUN公司的实现在java.lang.management包下。
JMX架构图:
MBean 即 managed beans 被管理的Beans
从图中我们可以看到,JMX的结构一共分为三层:
1、基础层:主要是MBean,被管理的资源。
MBean分为如下四种,我接下来主要介绍standard MBean
类型描述standard MBean这种类型的MBean最简单,它能管理的资源(包括属性,方法,时间)必须定义在接口中,然后MBean必须实现这个接口。它的命名也必须遵循一定的规范,例如我们的MBean为Hello,则接口必须为HelloMBean。dynamic MBean必须实现javax.management.DynamicMBean接口,所有的属性,方法都在运行时定义open MBean此MBean的规范还不完善,正在改进中model MBean与标准和动态MBean相比,你可以不用写MBean类,只需使用javax.management.modelmbean.RequiredModelMBean即可。RequiredModelMBean实现了ModelMBean接口,而ModelMBean扩展了DynamicMBean接口,因此与DynamicMBean相似,Model MBean的管理资源也是在运行时定义的。与DynamicMBean不同的是,DynamicMBean管理的资源一般定义在DynamicMBean中(运行时才决定管理那些资源),而model MBean管理的资源并不在MBean中,而是在外部(通常是一个类),只有在运行时,才通过set方法将其加入到model MBean中。后面的例子会有详细介绍
2、适配层:MBeanServer,主要是提供对资源的注册和管理。
3、接入层:提供远程访问的入口。
JMX超详细解读 - 冬瓜蔡 - 博客园 这篇博客详细介绍了三种使用JMX的方式,下面我提供一下自己实现的例程
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
import com.sun.jdmk.comm.HtmlAdaptorServer;
public class ApplicationServer
{
private static HtmlAdaptorServer adaptorServer = null;
private static javax.management.remote.JMXConnectorServer cs = null;
private static Logger logger = null;
private static ExecutorService cachedExecutors = null;
private static void initExecutor()
{
cachedExecutors = Executors.newFixedThreadPool(2);
}
private static void exitExecutor()
{
cachedExecutors.shutdown();
}
public static void execute(final Runnable runnable)
{
cachedExecutors.execute(runnable);
}
private static void initLogger()
{
System.out.println("configuring log4j with log4j.xml");
DOMConfigurator.configure("log4j.xml");
logger = Logger.getLogger("root");
}
private static void initHtmlJMX() throws Exception
{
MBeanServer server = MBeanServerFactory.createMBeanServer();
ObjectName helloName = new ObjectName("jmx:name=HelloWorld");
server.registerMBean(new Hello(), helloName);
ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8081");
adaptorServer = new HtmlAdaptorServer();
server.registerMBean(adaptorServer, adapterName);
adaptorServer.start();
logger.info("start jmx html server");
}
private static int initProtogenesisJMX() throws Exception
{
String port1Str = System.getProperty("com.jmxport1");
String port2Str = System.getProperty("com.jmxport2");
if (port1Str == null || port2Str == null)
{
logger.error("jmx端口未通过系统属性设置");
return -1;
}
final int port1 = Integer.valueOf(port1Str);
final int port2 = Integer.valueOf(port2Str);
System.setProperty("java.rmi.server.randomIDs", "true");
try
{
LocateRegistry.createRegistry(port2);
}
catch (java.rmi.server.ExportException ex)
{
logger.error("err", ex);
return -1;
}
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
java.util.HashMap<String, Object> env = new java.util.HashMap<String, Object>();
env.put("jmx.remote.x.password.file", "jmxremote.password");
env.put("jmx.remote.x.access.file", "jmxremote.access");
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://127.0.0.1:" + port1 + "/jndi/rmi://127.0.0.1:" + port2
+ "/jmxrmi");
cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
try
{
cs.start();
}
catch (java.net.BindException ex)
{
logger.error("端口已被占用", ex);
return -2;
}
return 0;
}
public static void exitHtmlJMX()
{
adaptorServer.stop();
}
public static void exitProtogenesisJMX() throws IOException
{
cs.stop();
}
public static void init() throws Exception
{
initLogger();
// initHtmlJMX();
initProtogenesisJMX();
initExecutor();
}
public static void exit() throws IOException
{
//exitHtmlJMX();
exitProtogenesisJMX();
exitExecutor();
}
public static void main(String[] args) throws Exception
{
System.setProperty("com.jmxport1", String.valueOf(7000));
System.setProperty("com.jmxport2", String.valueOf(7001));
init();
MBeanServer server = java.lang.management.ManagementFactory.getPlatformMBeanServer();
ObjectName helloName = new ObjectName("jmxBean:name=stopper");
server.registerMBean(new Stopper(), helloName);
}
public interface StopperMBean
{
void stop() throws IOException;
}
public static class Stopper implements StopperMBean
{
public void stop() throws IOException
{
ApplicationServer.exit();
}
}
}
import java.text.SimpleDateFormat;
import java.util.HashMap;
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
{
/**
*
* @param args
* host port username password bean name method ...params
* @throws Exception
*/
public static void main(String[] args) throws Exception
{
if ((args.length < 6) || (args.length % 2 != 0))
{
logErr("params error");
return;
}
final String host = args[0];
final int rmiPort = Integer.valueOf(args[1]).intValue();
final String username = args[2];
final String password = args[3];
final ObjectName objectName = new ObjectName(args[4]);
final String methodName = args[5];
final HashMap<String, String[]> jmxParamsHashMap = new HashMap<String, String[]>();
final String[] usernameAndPassword =
{ username, password };
jmxParamsHashMap.put("jmx.remote.credentials", usernameAndPassword);
final String serviceUrl = new StringBuilder().append("service:jmx:rmi:///jndi/rmi://").append(host).append(":")
.append(rmiPort).append("/jmxrmi").toString();
final JMXServiceURL jmxServiceURL = new JMXServiceURL(serviceUrl);
final JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, jmxParamsHashMap);
if (jmxConnector == null)
{
logErr(new StringBuilder().append("connect to jmx failed, url=").append(jmxServiceURL).toString());
return;
}
log(new StringBuilder().append("JMXConnector=").append(jmxConnector.toString()).toString());
Object[] paramsValue = null;
String[] paramsClassName = null;
final MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
final Object localObject = mBeanServerConnection.invoke(objectName, methodName, paramsValue, paramsClassName);
log(new StringBuilder().append("invoke method success, name=").append(objectName).append(", operation=")
.append(methodName).append(", retvalue=").append(localObject == null ? "void" : localObject.toString())
.toString());
}
static void log(String paramString)
{
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss : ");
final StringBuilder sBuilder = new StringBuilder();
sBuilder.append(simpleDateFormat.format(Long.valueOf(System.currentTimeMillis())));
sBuilder.append(paramString);
System.out.println(sBuilder.toString());
}
static void logErr(String paramString)
{
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss : ");
final StringBuilder sBuilder = new StringBuilder();
sBuilder.append(simpleDateFormat.format(Long.valueOf(System.currentTimeMillis())));
sBuilder.append(paramString);
System.err.println(sBuilder.toString());
}
}