JMX 与系统管理--转
<h2 id="N10062">前言 在 Java 程序的运行过程中,对 JVM 和系统的监测一直是 Java 开发人员在开发过程所需要的。一直以来,Java 开发人员必须通过一些底层的 JVM API,比如 JVMPI 和 JVMTI 等,才能监测 Java 程序运行过程中的 JVM 和系统的一系列情况,这种方式一直以来被人所诟病,因为这需要大量的 C 程序和 JNI 调用,开发效率十分低下。于是出现了各种不同的专门做资源管理的程序包。为了解决这个问题,Sun 公司也在其 Java SE 5 版本中,正式提出了 Java 管理扩展(Java Management Extensions,JMX)用来管理检测 Java 程序(同时 JMX 也在 J2EE 1.4 中被发布)。 JMX 的提出,让 JDK 中开发自检测程序成为可能,也提供了大量轻量级的检测 JVM 和运行中对象 / 线程的方式,从而提高了 Java 语言自己的管理监测能力。
要了解 JMX,我们就必须对当前的 IT 管理系统有一个初步的了解。随着企业 IT 规模的不断增长,IT 资源(IT resource)数量不断增加,IT 资源的分布也越来越分散。可以想象,甚至对于一家只有几百台 PC 公司的 IT 管理人员来说,分发一个安全补丁并且保证其在每台 PC 上的安装,如果只依赖人工来完成那简直就是一场噩梦。这样,IT 管理系统就应运而生。 然而,CPU、网卡、存储阵列是 IT 资源;OS、MS Office、Oracle database、IBM Websphere 也是 IT 资源。IT 管理系统若要对这些 IT 资源进行管理,就必须对这些管理对象有所了解:形形色色的 IT 资源就像是说着不同语言的人:Oralce 数据库表达内存紧张的方式和 Window XP 是绝然不同的, 而 IT 管理系统就像建造通天塔的经理,必须精通所有的语言, 这几乎是一个不可能完成的任务。难道 IT 管理系统是另外一个通天塔吗?当然不是!其实我们只要给每个 IT 资源配个翻译就可以了。 上图分析了管理系统的基本构架模式。其中 Agent / SubAgent 起到的就是翻译的作用:把 IT 资源报告的消息以管理系统能理解的方式传送出去。 也许读者有会问,为什么需要 Agent 和 SubAgent 两层体系呢?这里有两个现实的原因:
一般来说,管理系统会将同一物理分布或者功能类似的 SubAgent 分组成一组,由一个共用的 Agent 加以管理。在这个 Agent 里封装了 1 和 2 的功能。 JMX 既是 Java 管理系统的一个标准,一个规范,也是一个接口,一个框架。图 2 展示了 JMX 的基本架构。 和其它的资源系统一样,JMX 是管理系统和资源之间的一个接口,它定义了管理系统和资源之间交互的标准。
JMX 使用了 Java Bean 模式来传递信息。一般说来,JMX 使用有名的 MBean,其内部包含了数据信息,这些信息可能是:应用程序配置信息、模块信息、系统信息、统计信息等。另外,MBean 也可以设立可读写的属性、直接操作某些函数甚至启动 MBean 可发送的 notification 等。MBean 包括 Standard,MXBean,Dynamic,Model,Open 等几种分类,其中最简单是标准 MBean 和 MXBean,而我们使用得最多的也是这两种。MXBean 主要是? 标准 MBean 是最简单的一类 MBean,与动态 Bean 不同,它并不实现? 然后,我们打算使用一个标准 MBean,ServerMonitor 来监控 ServerImpl: 这里的 ServerMonitorBean 又是怎么回事呢? MXBean 规定了标准 MBean 也要实现一个接口,所有向外界公开的方法都要在这个接口中声明。否则,管理系统就不能从中获得相应的信息。此外,该接口的名字也有一定的规范:即在标准 MBean 类名之后加上“MBean”后缀。若 MBean 的类名叫做 MBeansName 的话,对应的接口就要叫做 MBeansNameMBean。 对于管理系统来说,这些在 MBean 中公开的方法,最终会被 JMX 转化成属性(Attribute)、监听(Listener)和调用(Invoke)的概念。如果读者对 Java Bean 有一些了解的话,不难看出, 下面我们就看一个模拟管理系统的例子: JMX 的核心是 MBServer。Java SE 已经提供了一个默认实现,可以通过? 在管理过程中,管理系统并不与资源或者 SubAgent 直接打交道,也就是说,这里不会直接引用到 MBean。而是通过 MBeanServer 的 但是对于很多已有的 SubAgent 实现,其 Coding Convention 并不符合标准 MBean 的要求。重构所有这些 SubAgent 以符合标准 MBean 标准既费力也不实际。JMX 中给出了动态(Dynamic) MBean 的概念,MBServer 不再依据 Coding Convention 而是直接查询动态 MBean 给出的元数据(meta data)以获得 MBean 的对外接口。 import javax.management.;
import java.lang.reflect.; public class ServerMonitor implements DynamicMBean {
} 另外还有两类 MBean:Open MBean 和 Model MBean。实际上它们也都是动态 MBean。 Open MBean 与其它动态 MBean 的唯一区别在于,前者对其公开接口的参数和返回值有所限制 —— 只能是基本类型或者 然而,普通的动态 Bean 通常缺乏一些管理系统所需要的支持:比如持久化 MBean 的状态、日志记录、缓存等等。如果让用户去一一实现这些功能确实是件枯燥无聊的工作。为了减轻用户的负担,JMX 提供商都会提供不同的 ModelBean 实现。其中有一个接口是 Java 规范中规定所有厂商必须实现的: public class Server {
} 然后我们对它的监测如下: import javax.management.;
import javax.management.modelmbean.; public class Main {
很明显,和其它 MBean 类似,使用 Model MBean 的过程也是下面几步:
唯一不同的是,ModelMBean 需要额外两步 : 第一步用于提供 serverMBean 的元数据,主要包括以下两类
第二步指出了 ServerMBean 管理的对象,也就是说,从元数据中得到的 Method 将施加在哪个 Object 上。需要指出的是 Model Bean 与普通动态 Bean 区别在于它的元数据类型 ModelMBeanInfo 扩展了前者的 MBeanInfo,使得 ModelMBeanOperationInfo、ModelMBeanConstructor_Info、ModelMBeanAttributeInfo 和 ModelMBeanNotificationInfo 都有一个额外的元数据: 首先,它最重要的 Descriptor 主要是 name、displayName 和 descriptorType,其中 name 是属性名称。"name" 要与对应 ModelMBeanAttributeInfo 的 name 相同。descriptorType 必须是 "attribute"。 另外,value、default、legalValues "value" 是用来设定初始值的,"default" 指当不能从 resource 中获得该属性时的默认返回值,"legalValues" 是一组合法的属性数据。它并不用来保证 setAttribute 的数据一致性,而是在 UI 系统,如 JConsole 中提示用户可能的数据输入。 在属性访问的 getMethod,setMethod 方法上,事实上所有对属性的访问都会被 delegate 给同一 MBeanInfo 中特定的 Operation。 getMethod/setMethod 给出了对应的 ModelMBeanOperationInfo 名称。 还有一些额外的属性,比如:persistPolicy,persistPeriod 是代表了持久化策略;currencyTimeLimit,lastUpdatedTimeStamp 缓存策略;iterable 属性是否必须使用 iterate 来访问。默认为否;protocolMap 定义了与第三方系统有关的数据转换的 data model;visibility 定义了与第三方 UI 系统有关的 MBean 如何显示的策略;presentationString 也是定义了与第三方 UI 系统有关的 MBean 如何显示策略,比如 "presentation=server.gif"。 事实上,策略特性有两个层次的作用域:整个 Model Bean 和特定的 MBeanFeature。 Model Bean 的策略描述会被施加到该 Model Bean 的所有 MBeanFeature 上去,除非该 MBeanFeature 重写了这个策略特性。 在上面的例子里,这一个语句: 给整个 serverMBeanInfo 设了一个策略描述 serverDescription,其中用 "currencyTimeLimit=10" 指出属性的缓存时间是 10 秒。所以,在 Main 方法中,两次 serverMBean.getAttribute("upTime");之间的间隔小于 10 秒就会得到同样的缓存值。 如果我们不想让 "upTime" 这个属性被缓存,我们可以在它的策略描述中加入 "currencyTimeLimit=-1": Descriptor getUpTimeDesc =
new DescriptorSupport( new String[] { "name=getUpTime","role=operation","currencyTimeLimit=-1" // 不需要缓存 }); getUpTimeDesc 也要改动的原因是 RequiredModelBean 会把获取 upTime 属性的工作 delegate 给 getUpTime invocation。只要其中一处使用 MBean 级的缓存策略,就没法获得实时 upTime 数据了。
JMX 的提出,为 Java 虚拟机提供了 Java 层上的检测机制。J2SE 中,新提出的? 检测虚拟机当前的状态总是 Java 开放人员所关心的,也正是因为如此,出现了大量的 profiler 工具来检测当前的虚拟机状态。从 Java SE 5 之后,在 JDK 中,我们有了一些 Java 的虚拟机检测 API,即? 要获得这些信息,我们首先通过?
ClassLoadMXBean 包括一些类的装载信息,比如有多少类已经装载 / 卸载(unloaded),虚拟机类装载的 verbose 选项(即命令行中的 Java – verbose:class 选项)是否打开,还可以帮助用户打开 / 关闭该选项。 该类提供的是操作系统的简单信息,如构架名称、当前 CPU 数、最近系统负载等。 运行时信息包括当前虚拟机的名称、提供商、版本号,以及 classpath、bootclasspath 和系统参数等等。 在 Java 这个多线程的系统中,对线程的监控是相当重要的。ThreadMXBean 就是起到这个作用。ThreadMXBean 可以提供的信息包括各个线程的各种状态,CPU 占用情况,以及整个系统中的线程状况。从 ThreadMXBean 可以得到某一个线程的 ThreadInfo 对象。这个对象中则包含了这个线程的所有信息。 我们知道,management 和底层虚拟机的关系是非常紧密的。其实,有一些的是直接依靠虚拟机提供的公开 API 实现的,比如 JVMTI;而另外一些则不然,很大一块都是由虚拟机底层提供某些不公开的 API / Native Code 提供的。这样的设计方式,保证了 management 包可以提供足够的信息,并且使这些信息的提供又有足够的效率;也使 management 包和底层的联系非常紧密。
Management 在 Java SE 5 被提出之后,受到了欢迎。在 Java 6 当中,这个包提供更多的 API 来更好地提供信息。 Java 程序通常关注是虚拟机内部的负载、内存等状况,而不考虑整个系统的状况。但是很多情况下,Java 程序在运行过程中,整个计算机系统的系统负荷情况也会对虚拟机造成一定的影响。随着 Java 的发展,Java 程序已经覆盖了各个行业,这一点也必须得到关注。在以前,利用 Native 代码来检测系统负载往往是唯一的选择,但是在 Java 6 当中,JDK 自己提供了一个轻量级的系统负载检测 API,即 当然这个 API 事实上仅仅返回一个对前一分钟系统负载的简单的估测。它设计的主要目标是简单快速地估测当前系统负荷,因此它首先保证了这个 API 的效率是非常高的;也因为如此,这个 API 事实上并不适用于所有的系统。 我们知道,同步是 Java 语言很重要的一个特性。在 Java SE 中,最主要的同步机制是依靠 synchronize 关键字对某一个对象加锁实现的;在 Java SE 5 之后的版本中,concurrent 包的加入,大大强化了 Java 语言的同步能力,concurrent 提供了很多不同类型的锁机制可供扩展。因此,要更好地观测当前的虚拟机状况和不同线程的运行态,去观察虚拟机中的各种锁,以及线程与锁的关系是非常必要的。很可惜的是,在过去的 JDK 中,我们并没有非常方便的 API 以供使用。一个比较直接的检测方式是查看线程的 stack trace,更为强大全面(但是也更复杂并且效率低下)的方案是得到一个 VM 所有对象的快照并查找之,这些策略的代价都比较大,而且往往需要编写复杂的 Native 代码。 JDK 6 里提供了一些相当简单的 API 来提供这个服务。首先了解两个新类,LockInfo 和 MonitorInfo 这两个类承载了锁的信息。LockInfo 可以是任何的 Java 锁,包括简单 Java 锁和?
getLockedMonitors()
返回一个所有当前线程已经掌握的锁对象的列表。 死锁检测一直以来是软件工程师所重视的,显然一个死锁的系统永远是工程师最大的梦魇。Java 程序的死锁检测也一直以来是 Java 程序员所头痛的。为了解决线程间死锁问题,一般都有预防(代码实现阶段)和死锁后恢复(运行时)两种方式。以前 Java 程序员都重视前者,因为在运行态再来检测和恢复系统是相当麻烦的,缺少许多必要的信息;但是,对于一些比较复杂的系统,采取后者或者运行时调试死锁信息也是非常重要的。由上面所说,现在我们已经可以知道每一个线程所拥有和等待的锁,因此要计算出当前系统中是否有死锁的线程也是可行的了。当然,Java 6 里面也提供了一个 API 来完成这个功能,即: ThreadMXBean.findDeadlockedThreads()
这个函数的功能就是检测出当前系统中已经死锁的线程。当然,这个功能复杂,因此比较费时。基本上仅仅将之用于调试,以便对复杂系统线程调用的改进。
JMX 在 Java SE 5/6 中的功能已经相当强大,但是距离 Java 程序开发人员的要求还是有一段距离,因此 Sun 公司已经向 JCP 提出了 JSR 255 (JMX API 2.0 版本)来扩充和进一步发展 JMX,并希望这个 JSR 将在 Java SE 7 中实现。在这个文档中,新的 JMX 2.0 将着重于:
具体的扩展可能包括:
可以看到,JMX 的进一步发展主要关注的是可扩展性、动态性和易用性等 Java 用户非常关注的方面。
在 Java SE 5 出现的 JMX 在 Java SE 6 中有了更多的功能和扩展能力,这很好地适应了 Java 语言的发展和用户的要求,让 Java 的监测、管理的的功能更加强大。 来源:http://www.ibm.com/developerworks/cn/java/j-lo-jse63/? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |