Java理论与实践:用JMX检测应用措施
副标题#e#
有几多次您曾经凝望着运行中的应用措施,问本身:“它到底在做什么?为 什么用了这么长时间呢?” 在这些时刻,您大概会想假如本身在应用措施中构 建了更多的监督成果就好了。譬喻,在处事器应用措施中,可以或许查察列队等待处 理的任务的数量和范例、当前正在处理惩罚的任务、已往一分钟或一小时内的吞吐量 统计、平均任务处理惩罚时间等。这些统计值容易汇集,可是在需要数据的时候,如 果没有非侵入性的数据检索机制,那么这些值就不太有用。
可以用很多方法导出操纵性数据——可以把周期性统计快照写入日志文件、 建设 Swing GUI、利用内嵌的 HTTP 处事器在 Web 页面上显示统计值可能宣布 可以用来查询应用措施的 Web 处事。可是在缺少监督和数据宣布基本设施的情 况下,大都应用措施开拓人员都做不到这些,因此造成对应用措施事情环境的了 解要比预期的少许多。
JMX
在 Java 5.0 中,类库和和 JVM 提供了一种全面的打点和监督基本设施—— JMX。JMX 是一种用来提供可以长途会见的打点接口的尺度法子,也是一种向应 用措施添加机动且强大的打点接口的浅易方法。被称作受管 bean(MBean)的 JMX 组件,是提供与实体的打点有关的会见器和业务要领的 JavaBean。每个受 管的实体(大概是整个应用措施或应用措施中的处事)实例化一个 MBean 并用 可读懂的名称注册它。支持 JMX 的应用措施依赖于 MBeanServer,它充当 MBean 的容器,提供长途会见、定名空间打点和安详处事。在客户端,jconsole 东西可以充当统一的 JMX 客户机。团结两者,对 JMX 的平台支持极大地低落了 使应用措施支持外部打点接口所需的事情和尽力。
除了提供 MBeanServer 实现,Java SE 5.0 还提供 JVM 以更利便地相识内 存打点、类装入、勾当线程、日志僻静台设置的状态。大都平台处事的监督和管 理在默认环境下都是开启的(机能影响最小),所以只需要连策应用措施与 JMX 客户机即可。图 1 给出了 jconsole JMX 客户机(JDK 的一部门) ,它显示了 个中一个内存打点视图——一段时间内的堆利用环境。Perform GC 按钮则证明 了 JMX 可以提供 除了查察操纵统计值之外的初始化操纵的成果。
图 1. 用 jconsole 查察堆利用环境
传输和安详性
JMX 指定了在 MBeanServer 和 JMX 客户之间通信所利用的协议,协议可以 在各类传输机制上运行。可以利用针对当地毗连的内置传输,及通过 RMI、 socket 或 SSL 的长途传输(可以通过 JMX Connector API 建设新的传输)。 认证是由传输执行的;当地传输答允用沟通的用户 ID 毗连到运行在当地系统上 的 JVM;长途传输可以用口令或证书举办认证。当地传输在 Java 6 下默认就是 启用的。要在 Java 5.0 下启用它,需要在 JVM 启动时界说系统属性 com.sun.management.jmxremote。“Monitoring and Management using JMX” 这份文档(请参阅参考资料)描写了启用和设置传输的设置步调。
检测 Web 处事器
检测应用措施来利用 JMX 很容易。像其他很多长途挪用框架(RMI、EJB 和 JAX-RPC)一样,JMX 也是基于接口的。要建设打点处事,需要建设指定打点方 法的 MBean 接口。然后可以建设一个 MBean 来实现此接口、实例化它及把它注 册到 MBeanServer。
清单 1 显示了网络处事(譬喻 Web 处事器)的 MBean 接口。它提供了检索 设置信息(譬喻端标语)和操纵性信息(譬喻处事是否启动)的 getter。它还 包括查察和修改可设置参数(譬喻当前日志级别)的 getter 和 setter,尚有 挪用打点操纵(譬喻 start() 和 stop())的要领。
清单 1. 某个 Web 处事器的 MBean 接口
public interface WebServerMBean {
public int getPort();
public String getLogLevel();
public void setLogLevel(String level);
public boolean isStarted();
public void stop();
public void start();
}
实现 MBean 类凡是很是直接明白,因为 MBean 接口要反应现有实体或处事 的属性和打点操纵。譬喻,MBean 中的 getLogLevel() 和 setLogLevel() 要领 会直接转给被 Web 处事器利用的 Logger 上的 getLevel() 和 setLevel() 方 法。JMX 做了一些定名限制。譬喻,MBean 接口名称必需以 MBean 末了, FooMBean 接口的 MBean 类必需叫作 Foo。(可以用更高级的 JMX 特性——动 态 MBean 往复除这个限制。)把 MBean 注册到默认的 MBeanServer 也很容易 ,如清单 2 所示:
清单 2. 用内置的 JMX 实现注册 MBean
public class WebServer implements WebServerMBean { ... }
...
WebServer ws = new WebServer(...);
MBeanServer server = ManagementFactory.getPlatformMBeanServer ();
server.registerMBean(ws, new ObjectName ("myapp:type=webserver,name=Port 8080"));
#p#副标题#e#
#p#分页标题#e#
通报给 registerMBean() 的 ObjectName 标识了受管实体。因为预见到指定 应用措施大概包括很多受管实体,所以名称包括域(清单 2 中的 “myapp”) 和很多标识域中的受管资源的键-值对。“name” 和 “type” 这两个键是常用 的,在利用的时候,name 该当在域中所有的同类 MBean 中可以或许独一地标识受管 实体。也可以指定其他键-值对,并且 JMX API 还包括举办工具名称通配匹配的 东西。
建设并注册了 MBean 之后,当即就可以把 jconsole 指向应用措施(在呼吁 行输入 jconsole)并在 “MBeans” 视图中查察它的打点属性和操纵。图 2 显 示了 jconsole 中针对新 MBean 的 Attributes 标签,图 3 显示了 Operations 标签。利用反射,JMX 可以指出哪个属性是只读的(Started、Port ),哪个属性是可读写的(LogLevel),并且 jconsole 答允修改读写属性。如 果读写属性的 setter 抛出异常(譬喻 IllegalArgumentException),JMX 就 把异常陈诉给客户机。
图 2. jconsole 中 MBean 的 Attributes 标签
图 3. jconsole 中 MBean 的 Operations 标签
数据范例
MBean 中的会见器和操纵可以或许用任何其签名形式的原语范例,以及 String、 Date 和其他尺度库类。也可以利用这些答允的范例的数组和荟萃。MBean 要领 也可以利用其他可以序列化的数据范例,可是这样做会造成互操纵性问题,因为 类文件也必需对 JMX 客户机可用。(假如利用 RMI 传输,可以利用 RMI 的自 动类下载特性完成这项任务。)假如想在打点接口中利用布局化数据范例,还想 制止与类可用性相关的互操纵性问题,可以利用 JMX 的开放 MBean 特性来表达 复合或表格数据。
检测处事器应用措施
在建设打点接口时,某些参数和操纵的特点很自然地就表白这些参数和数据 该当被包括在内,譬喻设置参数、操纵统计值、调试操纵(譬喻修他日志级别或 把应用措施状态导出到文件)、生命周期操纵(启动、遏制)。检测一个应用程 序,让它支持对这些属性和操纵的会见,凡是相当容易。可是,要从 JMX 得到 最大代价,就要在设计时思量什么数据在运行时对用户和操纵员有用。
假如用 JMX 相识处事器应用措施的事情环境,需要一种标识和跟踪事情单位 的机制。假如利用尺度的 Runnable 和 Callable 接口描写任务,通过让任务类 自描写(譬喻实现toString() 要领),可以在任务生命周期内跟踪它们,并提 供 MBean 要领来返回等待中、处理惩罚中和完成的任务列表。
清单 3 中的 TrackingThreadPool 演示的是 ThreadPoolExecutor 的一个子 类,它实时给出正在处理惩罚中的是哪些任务,以及已经完成的任务的时间统计值。 它通过包围 beforeExecute() 和 afterExecute() 挂钩,并提供能检索所汇集 数据的 getter,实现这些任务。
清单 3. 汇集处理惩罚中的任务僻静均的任务时间统计值的线程池类
public class TrackingThreadPool extends ThreadPoolExecutor {
private final Map<Runnable, Boolean> inProgress
= new ConcurrentHashMap<Runnable,Boolean>();
private final ThreadLocal<Long> startTime = new ThreadLocal<Long>();
private long totalTime;
private int totalTasks;
public TrackingThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
inProgress.put(r, Boolean.TRUE);
startTime.set(new Long(System.currentTimeMillis()));
}
protected void afterExecute(Runnable r, Throwable t) {
long time = System.currentTimeMillis() - startTime.get ().longValue();
synchronized (this) {
totalTime += time;
++totalTasks;
}
inProgress.remove(r);
super.afterExecute(r, t);
}
public Set<Runnable> getInProgressTasks() {
return Collections.unmodifiableSet(inProgress.keySet ());
}
public synchronized int getTotalTasks() {
return totalTasks;
}
public synchronized double getAverageTaskTime() {
return (totalTasks == 0) ? 0 : totalTime / totalTasks;
}
}
#p#分页标题#e#
清单 4 中的 ThreadPoolStatusMBean 显示了 TrackingThreadPool 的 MBean 接口,它提供了勾当任务、勾当线程、完成任务、等待任务的计数,还提 供了当前等待执行和正在执行的任务的列表。在打点接口中包括等待和执行任务 的列表,让您既可以看到应用措施的事情难度,又可以看到它今朝的事情内容。 这个特性不只让您可以洞察应用措施的行为,还能洞察它正在操纵的数据集的性 质。
清单 4. TrackingThreadPool 的 MBean 接口
public interface ThreadPoolStatusMBean {
public int getActiveThreads();
public int getActiveTasks();
public int getTotalTasks();
public int getQueuedTasks();
public double getAverageTaskTime();
public String[] getActiveTaskNames();
public String[] getQueuedTaskNames();
}
假如任务的重量级足够,那么甚至可以再进一步,在每个任务提交时都为它 注册一个 MBean (然后在任务完成时再打消注册)。然后可以用打点接口查询 每个任务的当前状态、运行了多长时间,可能请求打消任务。
清单 5 中的 ThreadPoolStatus 实现了 ThreadPoolStatusMBean 接口,它 提供了每个会见器的明明实现。与 MBean 实现类中的典范环境一样,每个操纵 实现起来都很细碎,所以把实现委托给了底层受管工具。在这个示例中,JMX 代 码完全独立于受管实体的代码。TrackingThreadPool 对付 JMX 一无所知;通过 为相关的属性提供打点要领和会见器,它提供了本身的编程打点接口。 还可以 选择在实现类中直接实现打点成果(让 TrackingThreadPool 实现 TrackingThreadPoolMBean 接口),可能单独实现(如清单 4 和 5 所示)。
清单 5. TrackingThreadpool 的 MBean 实现
public class ThreadPoolStatus implements ThreadPoolStatusMBean {
private final TrackingThreadPool pool;
public ThreadPoolStatus(TrackingThreadPool pool) {
this.pool = pool;
}
public int getActiveThreads() {
return pool.getPoolSize();
}
public int getActiveTasks() {
return pool.getActiveCount();
}
public int getTotalTasks() {
return pool.getTotalTasks();
}
public int getQueuedTasks() {
return pool.getQueue().size();
}
public double getAverageTaskTime() {
return pool.getAverageTaskTime();
}
public String[] getActiveTaskNames() {
return toStringArray(pool.getInProgressTasks());
}
public String[] getQueuedTaskNames() {
return toStringArray(pool.getQueue());
}
private String[] toStringArray(Collection<Runnable> collection) {
ArrayList<String> list = new ArrayList<String> ();
for (Runnable r : collection)
list.add(r.toString());
return list.toArray(new String[0]);
}
}
为了演示这些类如何提供对应用措施操纵的内容的相识,请思量这样一个 Web 搜寻应用措施,它把事情分成两类任务:获取长途页面,对页面举办索引。 每个任务别离用清单 6 所示的 FetchTask 或 IndexTask 描写。可以建设 ThreadPoolStatus MBean,提供处理惩罚这些任务所利用的线程池的打点接口,并把 它用 JMX 注册。
清单 6. Web 搜寻应用措施中利用的 FetchTask 类
public class FetchTask implements Runnable {
private final String name;
public FetchTask(String name) {
this.name = name;
}
public String toString() {
return "FetchTask: " + name;
}
public void run() { /* Fetch remote resource */ }
}
当此措施处理惩罚每个页面时,大概还会对新任务举办列队以获取这个页面上链 接的页面,所以在指按时间内,大概会既有获取任务又有尚未完成的索引任务。 可以或许正确地判定正在处理惩罚哪个页面,可能正在等待处理惩罚哪个页面,不只让您可以 领略应用措施的机能特征,还可以领略应用措施所操纵的数据的特征。
图 4 显示了正在处理惩罚 whitehouse.gov 站点的 Web 搜寻措施的快照。从图 中可以看到已经获取并索引了主页,措施此刻的事情是获取和索引直接从该主页 链接出的页面。单击 Refresh 按钮,可以对应用措施的事情流程举办取样,它 可以提供很多关于应用措施事情环境的信息,却不需引入大量日志可能在调试器 中运行应用措施。
图 4. Web 搜寻应用措施中的勾当任务和列队任务
竣事语
#p#分页标题#e#
团结平台内的 JMX 支持和 jconsole JMX 客户机可以提供一种向应用措施添 加打点和监督成果的轻松方法。纵然是没有详细打点需求的应用措施,为它们构 建这些成果也会让您对措施的运行及其所处理惩罚的数据的性质得到深入相识,并且 不需太多的事情和尽力。假如应用措施导出打点接口,此接口让您可以查察它操 作的内容,那么您就会越发相识它的运行状态——对它是否按预期的方法事情也 会更有信心——而不必求助于特另外东西(譬喻添加日志代码或利用调试器或分 析器)。
参考资料
进修
您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
“Monitoring and Managing Using JMX”(Sun Microsystems Inc.,2004 ):具体先容了内置的 JMX 署理的设置和利用。
JMX Best Practices (Sun Developer Network, 1994-2006):描写了定名管 理工具、选择 JMX 特性和为受管属性选择数据范例的最佳实践。
利用 JMX 打点 Apache Geronimo (developerWorks, J. Jeffrey Hanson, 2006 年 8 月):进修 Geronimo 应用措施处事器如何操作 JMX 来利便应用措施 的打点。
Java 技能专区:这里有数百篇有关 Java 编程各方面的文章。
关于作者
Brian Goetz 作为专业的软件开拓人员已经有 20 年了。他是 Sun Microsystems 的高级工程师,而且效力于多个 JCP 专家组。Brian 的新书 Java Concurrency In Practice 在 2006 年 5 月由 Addison-Wesley 出书。请 参阅 Brian 在风行的业界出书物上 已颁发和即将颁发的文章。