JMX 的理解与实际用途

应用场景和JMXMP访问演示代码

Posted by zhida on May 22, 2017

概念介绍、应用场景、个人理解

JMX的一些定义与用途

Java Management Extensions: Java管理扩展, 是管理系统和资源之间的一个接口,它定义了管理系统和资源之间交互的标准。

1、专门管理,监控JVM的一些信息的,特别是visual VM这个监控JVM的东西,还有一个添加JMX连接的时候

2、能使得基于Java语言开发的程序能被管理,并且是可扩展的。

3、用JMX把需要配置的属性集中在一个类中,然后写一个MBean,再进行相关配置。另外JMX还提供了一个工具页(可以用很多第三方的显示层jar包直接在UI界面上操作指定的Java对象),以方便我们对参数值进行修改。

4、让开发者和管理者可以获取程序运行的状态以及动态的修改程序的相关配置

我自己的理解:

  • 程序开启JMX功能可以监控程序的资源使用情况,因为Jdk自带了关于程序资源情况的MBean工具类
  • 我们可以使用类似的设计模式,对我们程序中的资源进行 监控和控制

JMX入门

一些专业术语:

1、MBean: 是Managed Bean的简称。在JMX中MBean代表一个被管理的资源实例,通过MBean暴露一系列方法和属性,外界可以获取被管理的资源的状态和操纵MBean的行为。事实上,MBean就是一个Java Object,同JavaBean模型一样,外界使用反射来获取Object的值和调用Object的方法,只是MBean提供了更加容易操作的反射的使用。Mbean 包括4种类型:标准MBean、动态MBean、开放MBean、模型MBean。

2、MBeanServer: MBeanServer是MBean 的容器。MBeanServer管理这些MBean,并且通过代理外界对它们的访问。MBeanServer提供了一种注册机制,通过注册Adaptor和Connector,以及MBean到MBeanServer,并且通过代理外界对它们的访问。外界可以通过名字来得到相应的MBean实例。

3、JMX Agent: Agent只是一个Java进程,它包括这个MBeanServer和一系列附加的MbeanService。当然这些Service也是通过MBean的形式来发布。

4、Protocol Adapters and Connectors: JMX Agent通过各种各样的Adapter和Connector来与外界(JVM之外)进行通信。同样外界(JVM之外)也必须通过某个Adapter和Connector来向JMX Agent发送管理或控制请求。Jdmk5.1中,sun提供很多Adaptor和Connector的实现

Adapter和Connector的区别在于:Adapter是使用某种协议(HTTP或者SNMP)来与JMX Agent获得联系,Agent端会有一个对象(Adapter)来处理有关协议的细节。比如SNMP Adapter和HTTP Adapter。而Connector在Agent端和client端都必须有这样一个对象来处理相应的请求与应答。比如RMI Connector。

JMX Agent可以带有任意多个Adapter,因此可以使用多种不同的方式访问Agent。


JMX基本构架:JMX分为三层,分别负责处理不同的事务。它们分别是:

1、Instrumentation 层: Instrumentation层主要包括了一系列的接口定义和描述如何开发MBean的规范。通常JMX所管理的资源有一个或多个MBean组成,因此这个资源可以是任何由Java语言开发的组件,或是一个JavaWrapper包装的其他语言开发的资源。

2、Agent 层: Agent用来管理相应的资源,并且为远端用户提供访问的接口。Agent层构建在Intrumentation层之上,并且使用管理Instrumentation层内部的组件。通常Agent由一个MBeanServer组成。另外Agent还提供一个或多个Adapter或Connector以供外界的访问

3、Distributed 层: Distributed层关心Agent如何被远端用户访问的细节。它定义了一系列用来访问Agent的接口和组件,包括Adapter和Connector的描述。

image

应用场景:

  • 中间件软件WebLogic的管理页面就是基于JMX开发的。

  • hibernate号称实现了JMX规范,将可管理,可调用的MBean注册到MBeanServer中,通过一种类似“web服务”的方式公布出去,并且伴有一个名字,可以通过该名字找到该MBean。并且,这里的MBean是可以被管理的

  • Tomcat的自带应用manager就是使用了JMX方式来管理Tomcat,以此完成Web应用的动态部署、启动、停止。

  • JBoss则整个系统都基于JMX构架, 使用JMX治理内部的各个Service。

  • 基于 Java 的开源网管软件 Hyperic HQ ,通过JMX与各被治理资源进行通讯和信息采集.

JMX是一个治理的框架。

当我们想使用JMX的时候,就要问,我们的系统当中有需要监控治理的资源或者对象吗?实事求是一点,我们不能为了想使用一个高端的技术,就歪曲系统的本来面目。

假如第一个问题是肯定的,接下来就是看这些资源是否有生命周期

经典案例: JBoss 就是将所有可部署的组件作为资源来治理,这些组建都有其生命周期。这个理念甚至延伸到了其系统内部,将其内部的服务作为组件纳入到 JMX中来,成就了 JBoss 基于JMX的微内核系统。

典型应用场景: 

一个大系统中,各内部模块系统之间的,基于接口方式的互相调用和治理,使用JMX是最佳方案.带来的好处是:

1、面向接口,远程调用对于开发人员是透明的,模块在调用JMX接口时,与调用本地方法几乎相同.

2、可视化的治理界面, 通过 Jconsole等JMX客户端,可以实时监控系统,并且可实时调用方法进行某些操作.

某聊天系统,一台服务器作为 在线用户列表服务器 List1, N 台服务器为用户提供聊天业务处理 N1 ,N2,N3…, 一台服务器作为后台治理系统 J2. 

系统治理员现在进行下面这样一个操作,察看某用户是否在线,找到该用户,发现其在线,则将该用户加入黑名单,并踢下线.

对应的JMX接口可以有以下几个: List1 为 J2提供: 查询在线用户JMX接口,加入黑名单接口,kickout接口; List1 为N1..等服务器提供以下接口: 注册业务服务器,添加在线用户.查找黑名单用户; N1…到N3为 List1 提供 :kickout接口。

因此在上面的踢下线操作,则由用户在J2的 Web 界面发出,交由 list1 执行, list1 记录黑名单之后,再找到用户所在业务服务器调用N1提供的接口让用户下线.

以上情形是在生产环境下的部署,而在开发工作,则可以将A1,A2,N…N3等功能合并在一个应用中调试. 由于使用的是JMX接口,在本地调试合并之后,可以直接调用应用内部接口方法;这样借助JMX实现的应用模块的灵活组装与拆分,使得系统的可以根据负载需要,根据性能情况,灵活的拆分和整合部署分布式的应用.

JMX与Web Service

1、个人认为,我们实现JMX规范,将东西发布出去,和通过web Service的方式是很类似的,也是可以远程调用的,只是相对的web Service的方式更加SOA一些

3、从这里,我觉得JMX可以实现的,我们也都可以通过web Service实现,只是看在它有个“M”上,以后如果有什么系统管理,监控方面的,可以考虑使用它,也许开发,个人觉得还是使用web service好一些。

4、选择webservice,xmlrpc等,但是这些都需要手工编写或用工具生成大量的代码来辅助完成接口间的java对象序列化。

现在的JMX连接方式:

提供了三种Connector:RMI Connector / JMXMP Connector(JMX message protocol Connector)/ Jolokia

  • RMI Connector 用JAVA的RMI功能来实现,可以在本地调用接口对象,这个不多说,因为只能是JAVA客户端使用,实现不了异构系统。
  • JMXMP Connector 就是使用协议在通讯了,Java也有实现这种连接的API,其他语言肯定也有的,这个能实现异构系统调用,是RMC模式。
  • Jolokia is a JMX-HTTP bridge giving an alternative method of accessing JMX beans,can be accessed using /jolokia on your management HTTP server.

The main focus of Jolokia is to allow easy access to JMX MBeans from everywhere. MBeans can be provided by the JVM itself, by an application server or an application itself, where each MBean is registered at a specific MBeanServer. Multiple MBeanServers can co-exist in a single JVM. The so called PlatformMBeanServer is always present and is created by the JVM during startup. Especially application servers often create additional MBeanServers for various purposes. When accessing an MBean remotely via JSR-160, the MBeanServer holding the requested MBean must be known before. Jolokia instead merges all MBeanServers it can find to give a single view on all MBeans. The merging algorithm is described in Section 9.1.1, “MBeanServer merging”.

OSGI与JMX

说到这里,感觉OSGI与JMX也好像,在看到JMX能够对MBean进行管理的时候,我就觉得跟OSGI很像,OSGI管理的是Bundle,找了找资源,原来早就有人考虑过了: http://teamojiao.iteye.com/blog/438334

if your question means, how to manage an OSGi runtime with JMX, you should have a look at MAEXO (http://code.google.com/p/maexo/). With MAEXO bundles up and running you will transparently get MBeans for a fair amount of services of the OSGi runtime as well as MBeans for your own services and bundles. Just have a look at the screencast.

实现原理

从JAVA 5开始,JDK就提供非常有名的java.lang.management 包,包里提供了许多MXBean的接口类,开发者可以很方便的获取到JVM的内存、GC、线程、锁、class、甚至操作系统层面的各种信息。我们可以遍历程序所有的MBean查看。

一个简单的代码示例

贴一部分重要的代码,建议使用完整的代码示例:Github demo / spring-demo-jmx ;分别是RMIJMXXP的示例

pom.xml
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>

	<dependency>
		<groupId>org.jvnet.opendmk</groupId>
		<artifactId>jmxremote_optional</artifactId>
		<version>1.0_01-ea</version>
	</dependency>

	<dependency>
		<groupId>com.sun.jdmk</groupId>
		<artifactId>jmxtools</artifactId>
		<version>1.2.1</version>
	</dependency>

</dependencies>
spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context.xsd">
 
    <context:component-scan base-package="org.springbyexample.jmx" />
    
    <context:mbean-export/>

    <!-- Expose JMX over JMXMP -->
    <bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" />

    <!-- Client connector to JMX over JMXMP -->
    <bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean"
          p:serviceUrl="service:jmx:jmxmp://localhost:9875" />

    <bean id="htmlAdaptor" class="com.sun.jdmk.comm.HtmlAdaptorServer"  init-method="start" p:port="8082" />

</beans>
服务端代码

@Component
@ManagedResource
(objectName="com.example.jmx.mbean:name=StopWatch")
public class StopWatchImp implements InitializingBean, StopWatch {


	private final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();

	private volatile Future<?> future;

	private final AtomicInteger seconds = new AtomicInteger();

	/* (非 Javadoc)
	 * @see com.example.springjmx.StopWatch#getSeconds()
	 */
	@Override
	@ManagedAttribute
	public int getSeconds() {
		return this.seconds.get();
	}

	public void afterPropertiesSet() {
		this.start();
	}

	/* (非 Javadoc)
	 * @see com.example.springjmx.StopWatch#start()
	 */
	@Override
	@ManagedOperation
	public void start() {
		this.scheduler.initialize();
		this.future = this.scheduler.scheduleAtFixedRate(new Runnable() {
			public void run() {
				seconds.incrementAndGet();
				System.out.println("主程序在计数: "+ seconds);
			}
		}, 1000);
	}

	/* (非 Javadoc)
	 * @see com.example.springjmx.StopWatch#stop()
	 */
	@Override
	@ManagedOperation
	public void stop() {
		this.future.cancel(true);
	}

	/* (非 Javadoc)
	 * @see com.example.springjmx.StopWatch#reset()
	 */
	@Override
	@ManagedOperation
	public void reset() {
		this.seconds.set(0);
	}
}
客户端代码
public void triggerStopWatch(String url, String objectNameStr) {
	try {
		JMXConnector jmxConnector = JMXConnectorFactory.connect(new JMXServiceURL(url));
		MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();

		if (mbsc == null) {
			return;
		}
		System.out.println("客户端访问 JMX 成功 ...");
		try {
			ObjectName objectName = new ObjectName(objectNameStr);
			jmxConnector.getMBeanServerConnection().invoke(objectName, "reset", null, null);

		} catch (InstanceNotFoundException | MalformedObjectNameException | ReflectionException | MBeanException e) {
			e.printStackTrace();
		}
	} catch (MalformedURLException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}

}
验证结果

执行代码中的单元测试可以访问修改MBean,或者启动程序之后在浏览器中输入 : localhost:8082 即可查看Mbean信息 :

image

image

注意事项

Mbean的不同名称形式
@Component
@ManagedResource(objectName="com.example.springjmx:name=StopWatch")
public class StopWatchImp implements StopWatch {}

MBean 名称为:com.example.springjmx:name=StopWatch

@Component
@ManagedResource
public class ServerManager { }

MBean 名称为:com.example.springjmx:name=serverManager,type=ServerManager

JVisual VM无法连接

使用代码可以连接MBeanServer , 但是 JVisual VM无法连接;方案参考:JMX Monitoring and Management

无论是客户端还是服务端都必须添加包依赖 jmxremote_optional-1.0_01-ea.jar ; 启动后选择添加远程JMX连接,不是添加远程主机

/usr/bin/jvisualvm --cp:a /Users/zhidaliao/Downloads/jmxremote_optional-1.0_01-ea.jar
ReflectionException: Target method not foun

invoke调用方法要保证 服务端代码和客户端代码的入参类型一致

jmxConnector.getMBeanServerConnection().invoke(objectName, "changeConnectionPoolSize",
						new Object[] { 1, 2 }, new String[] { Integer.class.getName(), Integer.class.getName() });
为什么启用JMX,可以监视JVM的情况

启动程序之后,遍历所有的 MBean信息如下:

========java.lang:type=Memory========
========java.lang:type=MemoryPool,name=PS Eden Space========
========java.lang:type=MemoryPool,name=PS Survivor Space========
========com.example.jmx.mbean:name=serverManager,type=ServerManager========
========java.lang:type=GarbageCollector,name=PS MarkSweep========
========java.lang:type=MemoryPool,name=Code Cache========
========java.lang:type=Runtime========
========java.lang:type=ClassLoading========
========java.nio:type=BufferPool,name=direct========
========java.lang:type=Threading========
========com.example.jmx.mbean:name=StopWatch========
========java.nio:type=BufferPool,name=mapped========
========java.util.logging:type=Logging========
========java.lang:type=Compilation========
========com.sun.management:type=HotSpotDiagnostic========
========java.lang:type=MemoryPool,name=PS Perm Gen========
========java.lang:type=GarbageCollector,name=PS Scavenge========
========java.lang:type=OperatingSystem========
========com.sun.jdmk.comm:name=htmlAdaptor,type=HtmlAdaptorServer========
========java.lang:type=MemoryPool,name=PS Old Gen========
========javax.management.remote.jmxmp:name=serverConnector,type=JMXMPConnectorServer========
========java.lang:type=MemoryManager,name=CodeCacheManager========
========JMImplementation:type=MBeanServerDelegate========

可以看到 有一些工具类暴露了JVM的一些属性情况,所以我们才能使用 visualVM 查看程序的资源情况

参考网站