精通 Grails: 在企业中使用 Grails,将 Grails 与 JMX、Spring 和 log4j 结合使用

常常有人问我 Grails 是否已经可以在企业中使用。简单的回答是 “是”。而我通常给出更加详细的回答:“只要您觉得 Spring 和 Hibernate(Grails 所依赖的底层技术)已经就绪;只要您觉得 Tomcat 或 JBoss(或 Java 企业版[Java EE])应用服务器已经就绪;只要您觉得 MySQL 或 PostgreSQL(或者您使用的数据库)已经就绪;只要您觉得 Java 编程已经企业就绪,那么 Grails 就已经企业就绪” 。

British Sky Broadcasting Group 最近将它的 Web 站点迁移到了 Grails。他们现在每月的点击量达到 1.1 亿次。LinkedIn.com 在其站点的某些商业部分使用 Grails。Tropicana Juice 在英国有一个 Web 站点,该站点几年来一直在 Grails 上运行。Grails.org 本身就是用 Grails 编写的,每月支持 70,000 多次下载。而 SpringSource 最近有关 G2One(Groovy 和 Grails 所在的公司)的问卷调查结果完全可以打消 Groovy 和 Grails 是否适合企业使用的任何疑虑。

Groovy 有时候看起来比较奇怪,最重要的是要记住,它完全是用普通的 Java 代码实现的。尽管 Grails 开发与其他典型的 Java Web 框架看起来很不一样,但最终您仍然会得到一个与 Java EE 兼容的 WAR 文件。

在这篇文章中,您将探讨一些用于监控和配置的企业级工具。学习如何使用 JMX 调整 Grails 应用程序。本文将简要介绍 Grails 中的 Spring 配置。您还会看到如何在 Config.groovy 中首次指定 log4j 设置,以及如何使用 JMX 动态调整它们。

关于本系列

Grails 是一种新型 Web 开发框架,它将常见的 Spring 和 Hibernate 等 Java 技术与当前流行的配置优于约定等实践相结合。Grails 是用 Groovy 编写的,它可以与遗留 Java 代码无缝集成,同时又加入了脚本编程语言的灵活性和动态性。学习完 Grails 之后,您将彻底改变看待 Web 开发的方式。

实现 JMX 工具

JMX 是 2000 年推出的。更确切地说,它是最古老的 JSR 之一 — JSR 3。随着 Java 语言在服务器上越来越流行,远程优化和配置实时运行应用程序成为平台的关键部分。在 2004 年,Sun 使用 JMX 实现了 JVM 并推出了支持工具,比如针对 Java 1.5 JDK 的 JConsole。

JMX 通过一个统一的接口提供 JVM 内省机制、应用服务器和类。这些不同的组件通过受管 bean(简写为 MBean)呈现给管理控制台。

有关 JMX 的更多背景信息,请参阅 “Java 理论与实践: 用 JMX 检测应用程序”。

MBeans 就像汽车仪表板上的各种仪表、刻度盘和开关。有些仪器是只读的,就像速度计一样;有些仪器是 “可写的”,就像加速器一样。但 MBean 是远程管理工具,所以这个仪表板比喻不是很不恰当。可以将其想象为远程打开汽车的转向灯或改变车里的电台频道。

启用本地 JMX 代理

本地还是远程?

对开发和测试而言,在本地同时运行 JMX 代理和客户机通常是最简单的事情。但在实际生产环境中远程监控代理时,JMX 的好处就会凸显出来。JConsole 与其他任何 Java 进程一样占用系统资源(RAM、CPU 周期等)。这会出现问题,特别是监控的生产服务器的负载压力较大时。但更重要的是,能够从一个地方监控多台服务器将使您成为数字领域的佼佼者。

当然,远程监控生产服务器还可以恰当保护它们的安全。您可以设置密码保护或使用更好的公/私钥身份验证(请参阅 参考资料)。

要使用 JMX 进行监控,则必须先启用它。在 Java 5 中,您必须在运行时为 JVM 提供几个与 JMX 相关的标志(在 Java 6 中,这些设置已经就绪,不过您一定要自己设置的话,也是可以的)。在 JMX 中,要设置一个 JMX 代理。清单 1 显示了 JVM 参数:


清单 1. 启用 JMX 监控的 JVM 参数

				
-Dcom.sun.management.jmxremote 
-Djava.rmi.server.hostname=localhost

一些教程建议创建一个全局 JAVA_OPTS 环境变量来保存 JMX 标志。其他教程则建议在命令行输入标志:java -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=localhost someExampleClass

两种方法都是可行的,但是对生产环境而言它们都不是最好的。我发现最好的方法是在服务器的启动脚本中设置这些值。如果每次重新启动服务器时都要输入这些复杂的标志,则表明这是一个不好的解决方案。应避免设置 CLASSPATHJAVA_OPTS 等全局变量,原因有两个:在复制服务器(在服务器之间复制一个一致的启动脚本更容易)时增加了不必要的配置步骤,而且它们强制同一机器上的所有 Java 进程共享同一配置。是的,您可以创建一个详细的清单来提醒您这些琐碎的配置细节,但是记录复杂的东西远不如将复杂去掉有效。

对于 UNIX®、Linux® 和 Mac OS X 系统,Grails 启动脚本是 $GRAILS_HOME/bin/grails。编辑这个文件,添加两个 JAVA_OPTS 行,如清单 2 所示:


清单 2. 在 Grails 启动脚本中为 UNIX、Linux 和 Mac OS X 启用 JMX 监控

#!/bin/sh DIRNAME='dirname "$0"' . "$DIRNAME/startGrails" export JAVA_OPTS="-Dcom.sun.management.jmxremote" export JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=localhost" startGrails org.codehaus.groovy.grails.cli.GrailsScriptRunner "$_cnnew1_cnnew1@"

对于 Windows®,Grails 启动脚本是 $GRAILS_HOME/bin/grails.bat。在调用 startGrails.bat 之前,向 grails.bat 添加两行,如清单 3 所示:


清单 3. 在 Grails 中为 Windows 启用 JMX 监控

				
set JAVA_OPTS=-Dcom.sun.management.jmxremote
set JAVA_OPTS=%JAVA_OPTS% -Djava.rmi.server.hostname=localhost

在两个脚本中,注意第一个 JAVA_OPTS 变量赋值覆盖了全局环境变量(如果有的话)。这个设置只覆盖着一个进程 — 它不会对整个系统的全局变量进行赋值。我这样做的目的是防止全局设置影响本地设置。如果您依赖于已经正确设置的全局值,请确保在开始赋值时包含现有变量,像我在清单 2 和清单 3 的第二行中那样。

现在,输入 grails run-app 启动 Grails。您看到的内容与控制台输出中的完全相同,不过应用服务器现在已经可以进行监控。

使用一个 JMX 客户机来监控 JMX 代理。这是一个类似 JConsole 的桌面 GUI(包含在 Java 5 及更高版本中)或 Web UI(包含在大多数服务器中,比如 Tomcat 和 JBoss)。甚至可以编写代码来监控代理,在本文快结束时将提到。

打开第二个命令行窗口,输入 jconsole。您将在本地 JML 代理列表中看到 Grails,如图 1 所示。单击 Grails,然后单击 Connect 按钮。


图 1. JConsole 列出了本地 JMX 代理

出于安全考虑,只能在使用 NTFS 的 Windows 系统上访问本地 JMX。如果系统使用的是 FAT 或 FAT32,可能会出现问题。但不要担心。在接下来的部分中,我将说明如何设置 JMX 代理进行远程访问。就算代理和客户机刚好位于同一机器上,也不会遇到本地安全问题。

连接之后,您应该看到类似图 2 所示的摘要页面:


图 2. JConsole 摘要页面

单击 Memory、Threads、Classes 和 VM 选项卡。您可以实时查看 JVM 的内部情况。如果服务器是在物理内存上运行,那么您可以看到实时线程数,甚至能够看到服务器的已经运行时间。这些选项卡非常有趣,不过您马上要将注意力转向 MBeans 选项卡 — 这里将会出现您需要的类。

启用远程 JMX 代理

不要在工作时尝试这个操作

永远不要在生产中使用这个配置。出于演示目的,我关闭了所有身份验证和加密。要了解如何保护 JMX 代理的远程管理,请参阅 参考资料

要设置 JMX 代理以接受远程连接,需要向 JVM 传递另外几个与 JMX 相关的标志。这几个标志打开一个管理端口并配置安全设置(或本例中的 lack thereof)。

向 Grails 启动脚本添加三个新行,如清单 4 所示:


清单 4. 在 Grails 启动脚本中启用远程 JMX 监控

				
export JAVA_OPTS="-Dcom.sun.management.jmxremote"    
export JAVA_OPTS=" $JAVA_OPTS -Djava.rmi.server.hostname=localhost"
export JAVA_OPTS=" $JAVA_OPTS -Dcom.sun.management.jmxremote.port=9004"
export JAVA_OPTS=" $JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
export JAVA_OPTS=" $JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"

使用这些设置重新启动 Grails。还要重新启动 JConsole。这次,单击 Remote 选项卡并连接到端口 9004 上的 localhost,如图 3 所示:


图 3. 在 JConsole 中连接到远程 JMX 代理

这是一种验证是否连接上远程 JVM(即使它在同一系统上运行)的快速方法。单击 MBeans 选项卡。展开左边的 java.lang 树。单击 Runtime 元素。然后在屏幕右侧的属性窗口中双击 InputArguments。您应该看到所有远程 JMX 设置,如图 4 所示:


图 4. 传递给 JVM 的 JMX 远程代理标志

让这个窗口保持打开。单击 Connection 菜单打开一个新的连接。单击 Remote 选项卡,这次接受默认值(端口 0 上的 localhost)。展开 Runtime MBean 的 InputArguments。注意这里没有远程 JMX 标志(如图 5 所示):


图 5. 监控两个不同的 JMX 代理

如果标题栏(监控本身)的提示不够清楚,注意您刚打开的第二个 JConsole 窗口,它监控 JConsole 应用程序本身。

现在您启动了 JConsole 并监控 Grails 应用程序,此时应该使用它进行一些实际操作了,比如调整登录设置,不过在进行该操作之前,必须先理解最后一个 JMX 难点:MBean 服务器。

MBean 服务器、Grails 和 Spring

您在 JConsole 上单击的 Runtime 元素是一个 MBean。为了让 MBean 呈现给 JMX 客户机,必须使用一个内部运行有 JMX 代理的 MBean 服务器注册它。有些人将术语 “JMX 代理” 等同于 “MBean 服务器”,但从技术上讲,MBean 服务器是在 JMX 代理内部运行的众多组件中的一个。

要以编程方式注册 MBean,需调用 MBeanServer.registerMBean()。不过,在 Grails 中,更准确地说,这是由一个配置文件(一个 Spring 配置文件)管理的。

Spring 是 Grails 的核心。它是控制所有类如何交互的依赖项注入框架(有关 Spring 的更多信息,请参阅 参考资料)。

从 JMX 角度,您可能会想:我在用 MBean 服务器注册这个 MBean。但从 Spring 角度,您应该这样考虑:我在将 MBean 注入到 MBean 服务器中。动作对象可能不同,但最终结果是一样的:MBean 变为对 JMX 客户机是可视的。

首先在 grails-app/conf/spring 中创建一个名为 resources.xml 的文件(在本文后面,您将明白 resources.groovy 和 resources.xml 的关系)。设置 resources.xml,如清单 5 所示:


清单 5. 在 resources.xml 中设置 Spring/JMX 基础设施

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"> <property name="locateExistingServerIfPossible" value="true" /> </bean> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="server" ref="mbeanServer"/> <property name="beans"> <map> </map> </property> </bean> </beans>

如果想确保基本配置是正确的,那么现在可重新启动 Grails,但只解决问题的一半:您有了一台 MBean 服务器,但是没有任何 MBean。此时看到的两个 bean(mbeanServerexporter)是需要注册 MBean 的基础设施。mbeanServer bean 保存一个到现有 MBean 服务器的引用。mbeanServer bean 被注入到 exporter bean — 将 MBean 列表呈现给 JMX 客户机(比如 JConsole)的类。现在仅需将 MBean 添加到 exporter bean 内部的 bean 映射中,以注册它。下一小节将进行此操作。

通过 Grails 使用 log4j

打开 grails-app/conf/Config.groovy 查看 log4j 设置(如清单 6 所示):


清单 6. Config.groovy 中的 log4j 设置

				
log4j {
    appender.stdout = "org.apache.log4j.ConsoleAppender"
    appender.'stdout.layout'="org.apache.log4j.PatternLayout"
    appender.'stdout.layout.ConversionPattern'='[%r] %c{2} %m%n'
    // and so on...
}

启动 Grails 应用程序时,命令提示符上出现的大多数消息是 log4j 消息。这要归功于 org.apache.log4j.ConsoleAppender(更多关于 log4j 的基础知识,请参阅 参考资料)。

注册 log4j MBean

如果需要在没有 JMX 的情况下调整 Grails 的登录设置,只需简单地编辑这个文件并重新启动服务器,但如果更愿意调整这些设置而不重新启动服务器,或者想远程调整它们,那应该怎样做呢?这看起来似乎是 JMX 可选的完美方法。幸运的是,log4j 附带一个方便执行这些任务 MBean。您所需做的只是注册 log4j MBean。

entry 的 XML(如清单 7 所示)添加到 resources.xml。这将把 log4j MBean 注入到 MBean 服务器。


清单 7. 将 MBean 注入到 MBean 服务器

				
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">	
  <property name="server" ref="mbeanServer"/>
  <property name="beans">
    <map>
      <entry key="log4j:hierarchy=default">
        <bean class="org.apache.log4j.jmx.HierarchyDynamicMBean"/>
      </entry>        
    </map>
  </property>
</bean>

重新启动 Grails,然后重新启动 JConsole。如果连接到端口 9004 上的 localhost,新的 log4j MBean 应该显示在 MBeans 选项卡中。展开 log4j 树元素,单击默认值,然后单击 Info 选项卡。从刚添加到 resources.xml(参见图 5)的条目中,可以看到配置片段:


图 6. 查看默认 MBean 信息

现在可以通过 JMX 看到 log4j 了,下一步是调整一些登录设置。

动态更改 log4j 设置

假设现在 Grails 应用程序表现异常。您应该查找问题的根源。查看 grails-app/conf/Config.groovy,您会发现根登录程序将它的输出发送到控制台,但过滤器被设置为 errorrootLogger="error,stdout"。您希望将登录级别更改为 trace 来提高控制台的输出量。

看一下 JConsole。在 log4j 文件夹下,您应该可以看到根 MBean。可以看到优先级属性被设置为 ERROR,就像在 Config.groovy 中一样。双击 ERROR 值并输入 TRACE,如图 6 所示:


图 7. 将根登录程序优先级从 ERROR 更改为 TRACE

为了验证控制台比以前更好用,在浏览器中,在 Grails 应用程序的主页上单击到 AirportMappingController 的链接。在大量新的输出中,您应该可以找到一些有关 Grails 如何导入初始列表的详细信息。请参阅清单 8 中的样例:


清单 8. 增加 log4j 输出

[11277653] metaclass.RedirectDynamicMethod Dynamic method [redirect] looking up URL mapping for controller [airportMapping] and action [list] and params [["action":"index", "controller":"airportMapping"]] with [URL Mappings ------------ org.codehaus.groovy.grails.web.mapping.ResponseCodeUrlMapping@1bab0b /rest/airport/(*)? /(*)/(*)?/(*)? ] [11277653] metaclass.RedirectDynamicMethod Dynamic method [redirect] mapped to URL [/trip/airportMapping/list] [11277653] metaclass.RedirectDynamicMethod Dynamic method [redirect] forwarding request to [/trip/airportMapping/list] [11277653] metaclass.RedirectDynamicMethod Executing redirect with response [com.opensymphony.module.sitemesh.filter.PageResponseWrapper@19243f]

更改 log4j ConversionPattern

现在需要更改输出模式。在 Config.groovy 中,使用下面这一行设置模式:appender.'stdout.layout.ConversionPattern'='[%r] %c{2} %m%n'。查看 log4j 文档,您决定将它设置为更具描述性的东西。

单击 JConsole 中的 stdout MBean。将 conversionPattern 属性从它的原始值更改为 [%5p] %d{hh:mm:ss} (%F:%M:%L)%n%m%n%n。生成一些新的日志输出后,我将描述这些奇怪的符号的含义(请参阅 参考资料,了解设置 conversionPattern 的更多信息)。


图 8. 在 PatternLayout 中更改 conversionPattern

现在再次在 Web 浏览器中单击主页链接和 AirportMappingController 链接。输出的格式发生了很大变化,如清单 9 所示:


清单 9. 使用新的 conversionPattern 的控制台输出

				
[DEBUG] 09:04:47 (RedirectDynamicMethod.java:invoke:127)
Dynamic method [redirect] looking up URL mapping for controller 
[airportMapping] and action [list] and params 
[["action":"index", "controller":"airportMapping"]] with [URL Mappings
------------
org.codehaus.groovy.grails.web.mapping.ResponseCodeUrlMapping@e73cb7
/rest/airport/(*)?
/(*)/(*)?/(*)?
]

[DEBUG] 09:04:47 (RedirectDynamicMethod.java:invoke:144)
Dynamic method [redirect] mapped to URL [/trip/airportMapping/list]

[DEBUG] 09:04:47 (RedirectDynamicMethod.java:redirectResponse:162)
Dynamic method [redirect] forwarding request to [/trip/airportMapping/list]

[DEBUG] 09:04:47 (RedirectDynamicMethod.java:redirectResponse:168)
Executing redirect with response 
   [com.opensymphony.module.sitemesh.filter.PageResponseWrapper@47b2e7]

现在您可以看到输出,以下是详细过程:%p 写出优先级别。这些消息很明显是 DEBUG 级别。%d{hh:mm:ss} 以小时:分钟:秒的格式显示日期戳。(%F:%M:%L) 将文件名、方法和行编号放在括号内。最后,%n%m%n%n 写入一个新行、消息和其他两行。

通过 JMX 对 log4j 所做的更改不是持久化的。如果重新启动 Grails,它会恢复到 Config.groovy 中的持久化设置。这意味着您可以任意处理 JMX 设置,而不用担心会永久打乱事情。对于 ConversionPattern,使用 JMX 是体验设置的很好方法,您可以找到最喜欢的设置。但是不要忘了将模式复制到 Config.groovy,以使更改是持久化的。

查看 Hibernate DEBUG 输出

回到先前的假设,您正在调试一个实时 Grails 应用程序,但还没有找到您需要的东西。将根 MBean 的优先级属性设置回 ERROR 来减少干扰。

可能问题就出在 Hibernate。再回过头看看 Config.groovy,您会发现 org.hibernate 包的登录输出被设置为 off。不要改变整个应用程序的输出级别,而是集中于特定的包,这样可能会获得更多的信息。

在 JConsole 中,单击默认 MBean。除了更改属性值以外,您还可以调用 MBean 上的方法。单击 Operations 选项卡。为名称参数输入 org.hibernate 并单击 addLoggerMBean 按钮。您应该会看到一个新的 MBean 出现在左边的树中。

单击新的 org.hibernate MBean 并将优先级属性更改为 DEBUG,如图 9 所示:


图 9. 更改 org.hibernate MBean 上的优先级

现在返回到 Web 浏览器,单击主链接,并再次单击 AirportMappingController 。应该会看到一大串 DEBUG 日志语句,如清单 10 所示:


清单 10. Hibernate log4j 输出

				
[DEBUG] 10:05:52 (AbstractBatcher.java:logOpenPreparedStatement:366)
about to open PreparedStatement (open PreparedStatements: 0, globally: 0)

[DEBUG] 10:05:52 (ConnectionManager.java:openConnection:421)
opening JDBC connection

[DEBUG] 10:05:52 (AbstractBatcher.java:log:401)
select this_.airport_id as airport1_0_0_, this_.locid as locid0_0_, 
this_.latitude as latitude0_0_, this_.longitude as longitude0_0_, 
this_.airport_name as airport5_0_0_, this_.state as state0_0_ 
from usgs_airports this_ limit ?

[DEBUG] 10:05:52 (AbstractBatcher.java:logOpenResults:382)
about to open ResultSet (open ResultSets: 0, globally: 0)

[DEBUG] 10:05:52 (Loader.java:getRow:1173)
result row: EntityKey[AirportMapping#1]

[DEBUG] 10:05:52 (Loader.java:getRow:1173)
result row: EntityKey[AirportMapping#2]

花一点时间查看 Hibernate DEBUG 输出。您详细了解到何时从数据库挑选数据,并转换为一个由 bean 组成的 ArrayList

使用 Spring Bean Builder

现在您已经知道了如何通过 resources.xml 配置 JMX,因此可以进行新的实践了。Grails 通过一个替代文件 resources.groovy 支持 Spring 配置。将 grails-app/conf/spring/resources.xml 重命名为 resources.xml.old。将如清单 11 所示的代码添加到 resources.groovy 中:


清单 11. 使用 Bean Builder 配置 Spring

import org.springframework.jmx.support.MBeanServerFactoryBean import org.springframework.jmx.export.MBeanExporter import org.apache.log4j.jmx.HierarchyDynamicMBean beans = { log4jBean(HierarchyDynamicMBean) mbeanServer(MBeanServerFactoryBean) { locateExistingServerIfPossible=true } exporter(MBeanExporter) { server = mbeanServer beans = ["log4j:hierarchy=default":log4jBean] } }

如您所见,Spring bean 使用 Groovy 代码(而不是 XML)配置的。您已经在 “Grails 与遗留数据库” 和 “RESTful Grails” 中看到现实中的 Groovy MarkupBuilder。主题有点变化 — 一个专门为 Spring 配置定义 bean 的 Bean Builder。

重新启动 Grails 和 JConsole。确认 XML 配置中没有任何更改。

使用 XML 来配置 Spring 可以轻松运用 Web 各种优势 — 可以从大量源复制粘贴代码片段。但是使用 Bean Builder 更符合 Grail 中的其余配置。使用 Grails 到现在,您已经看到了 DataSource.groovy、Config.groovy、BootStrap.groovy 和 Events.groovy,这只是其中一小部分。在代码中进行配置,这意味着您可以执行一些操作,比如基于运行的环境有条件地呈现 MBean。

例如,清单 12 显示了如何在生产环境中呈现 log4jBean,但在开发环境中隐藏它:


清单 12. 有条件地呈现 JMX bean

import org.springframework.jmx.support.MBeanServerFactoryBean import org.springframework.jmx.export.MBeanExporter import org.apache.log4j.jmx.HierarchyDynamicMBean import grails.util.GrailsUtil beans = { log4jBean(HierarchyDynamicMBean) mbeanServer(MBeanServerFactoryBean) { locateExistingServerIfPossible=true } switch(GrailsUtil.environment){ case "development": break case "production": exporter(MBeanExporter) { server = mbeanServer beans = ["log4j:hierarchy=default":log4jBean] } break } }

输入 grails run-app 并在 JConsole 中确定 log4j MBean 没有出现在开发模式中。现在输入 grails prod run-app(或 grails war 并将 WAR 文件部署到您选择的应用服务器)。这个 MBean 会一直等待您重新启动 JConsole。

Groovy 中的 JMX

我最后要向您展示的是如何以编程方式调试 JMX MBean。JConsole GUI 非常漂亮,能够从 Groovy 脚本进行更改更是增加了它的魅力。

开始之前,创建一个名为 testJmx.groovy 的文件,将清单 13 中的代码添加到该文件中:


清单 13. 在 Groovy 中调用一个远程 JMX 代理

import javax.management.MBeanServerConnection import javax.management.remote.JMXConnectorFactory import javax.management.remote.JMXServiceURL def agentUrl = "service:jmx:rmi:///jndi/rmi://localhost:9004/jmxrmi" def connector = JMXConnectorFactory.connect(new JMXServiceURL(agentUrl)) def server = connector.mBeanServerConnection println "Number of registered MBeans: ${server.mBeanCount}" println "/nRegistered Domains:" server.domains.each{println it} println "/nRegistered MBeans:" server.queryNames(null, null).each{println it}

如果 Grails 正在运行,应该可以看到如清单 14 所示的输出:


清单 14. testJmx.groovy 脚本的输出

$ groovy testJmx.groovy Number of registered MBeans: 20 Registered Domains: java.util.logging JMImplementation java.lang log4j Registered MBeans: java.lang:type=MemoryManager,name=CodeCacheManager java.lang:type=Compilation java.lang:type=GarbageCollector,name=Copy java.lang:type=MemoryPool,name=Eden Space log4j:appender=stdout java.lang:type=Runtime log4j:hierarchy=default log4j:logger=root log4j:appender=stdout,layout=org.apache.log4j.PatternLayout java.lang:type=ClassLoading java.lang:type=MemoryPool,name=Survivor Space java.lang:type=Threading java.lang:type=GarbageCollector,name=MarkSweepCompact java.util.logging:type=Logging java.lang:type=Memory java.lang:type=OperatingSystem java.lang:type=MemoryPool,name=Code Cache java.lang:type=MemoryPool,name=Tenured Gen java.lang:type=MemoryPool,name=Perm Gen JMImplementation:type=MBeanServerDelegate

这个脚本中有趣的部分来自 javax.management.MBeanServerjavax.management.MBeanServer 是调用 connector.mBeanServerConnection 时返回的(记住,Java 中的 getFoo() 方法调用在 Groovy 中可以简写为 foo)。调用 server.mBeanCount 返回已注册 MBean 的数量。调用 server.domains 返回一个由域名组成的 String[]。域名是 MBean 标识符的第一部分 — 用逗号分隔的名/值对列表完全限定名称。调用 server.queryNames(null, null) 返回一个由所有已注册 MBean 组成的 Set(要更多地了解 MBeanServer 类的可用方法,请参阅 参考资料)。

为了获得某个特定 MBean,请将清单 16 中的代码添加到脚本底部:


清单 16. 获得一个 MBean

				
println "/nHere is the Runtime MBean:"
def mbean = new GroovyMBean(server, "java.lang:type=Runtime")
println mbean

有了一个到 MBean 服务器的连接并知道 MBean 的名称后,使用一行即可获取一个新的 GroovyMBean。清单 17 显示了脚本输出:


清单 17. GroovyMBean 输出

				
Here is the Runtime MBean:
MBean Name:
  java.lang:type=Runtime
  
Attributes:
  (r) javax.management.openmbean.TabularData SystemProperties
  (r) java.lang.String VmVersion
  (r) java.lang.String VmName
  (r) java.lang.String SpecName
  (r) [Ljava.lang.String; InputArguments
  (r) java.lang.String ManagementSpecVersion
  (r) java.lang.String SpecVendor
  (r) long Uptime
  (r) long StartTime
  (r) java.lang.String LibraryPath
  (r) java.lang.String BootClassPath
  (r) java.lang.String VmVendor
  (r) java.lang.String ClassPath
  (r) java.lang.String SpecVersion
  (r) java.lang.String Name
  (r) boolean BootClassPathSupported

是否还记得本文前面提到的 InputArguments?它们是传递给 JVM 的所有 -D 参数。使用这些参数来确认您确实连接到了远程 JMX 代理。再添加两行代码(如清单 18 所示)以输出 String[]


清单 18. 从运行时 MBean 获取 InputArguments

				
println "/nHere are the InputArguments:"
mbean.InputArguments.each{println it}

如果看到清单 19 所示的输出,就说明您圆满完成了所有任务:


清单 19. 显示 InputArguments

				
Here are the InputArguments:
-Xserver
-Xmx512M
-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=localhost
-Dcom.sun.management.jmxremote.port=9004
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dprogram.name=grails
-Dgroovy.starter.conf=/opt/grails/conf/groovy-starter.conf
-Dgrails.home=/opt/grails
-Dbase.dir=.
-Dtools.jar=/Library/Java/Home/lib/tools.jar

有关 GroovyMBean 的更多信息,请参阅 参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值