在本期的 精通 Grails 中,作者 Scott Davis 将解释为何 Grails 已经可以在企业中使用。您将看到如何将 Grails 与企业级库结合使用,包括 Java™ 管理扩展(Java Management Extensions,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 动态调整它们。
|
实现 JMX 工具
JMX 是 2000 年推出的。更确切地说,它是最古老的 JSR 之一 — JSR 3。随着 Java 语言在服务器上越来越流行,远程优化和配置实时运行应用程序成为平台的关键部分。在 2004 年,Sun 使用 JMX 实现了 JVM 并推出了支持工具,比如针对 Java 1.5 JDK 的 JConsole。
JMX 通过一个统一的接口提供 JVM 内省机制、应用服务器和类。这些不同的组件通过受管 bean(简写为 MBean)呈现给管理控制台。
MBeans 就像汽车仪表板上的各种仪表、刻度盘和开关。有些仪器是只读的,就像速度计一样;有些仪器是 “可写的”,就像加速器一样。但 MBean 是远程管理工具,所以这个仪表板比喻不是很不恰当。可以将其想象为远程打开汽车的转向灯或改变车里的电台频道。
启用本地 JMX 代理
|
要使用 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。
两种方法都是可行的,但是对生产环境而言它们都不是最好的。我发现最好的方法是在服务器的启动脚本中设置这些值。如果每次重新启动服务器时都要输入这些复杂的标志,则表明这是一个不好的解决方案。应避免设置 CLASSPATH 和 JAVA_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 "在本期的 精通 Grails 中,作者 Scott Davis 将解释为何 Grails 已经可以在企业中使用。您将看到如何将 Grails 与企业级库结合使用,包括 Java™ 管理扩展(Java Management Extensions,JMX)、Spring 和 log4j。
实现 JMX 工具 JMX 是 2000 年推出的。更确切地说,它是最古老的 JSR 之一 — JSR 3。随着 Java 语言在服务器上越来越流行,远程优化和配置实时运行应用程序成为平台的关键部分。在 2004 年,Sun 使用 JMX 实现了 JVM 并推出了支持工具,比如针对 Java 1.5 JDK 的 JConsole。 JMX 通过一个统一的接口提供 JVM 内省机制、应用服务器和类。这些不同的组件通过受管 bean(简写为 MBean)呈现给管理控制台。 MBeans 就像汽车仪表板上的各种仪表、刻度盘和开关。有些仪器是只读的,就像速度计一样;有些仪器是 “可写的”,就像加速器一样。但 MBean 是远程管理工具,所以这个仪表板比喻不是很不恰当。可以将其想象为远程打开汽车的转向灯或改变车里的电台频道。 启用本地 JMX 代理
要使用 JMX 进行监控,则必须先启用它。在 Java 5 中,您必须在运行时为 JVM 提供几个与 JMX 相关的标志(在 Java 6 中,这些设置已经就绪,不过您一定要自己设置的话,也是可以的)。在 JMX 中,要设置一个 JMX 代理。清单 1 显示了 JVM 参数: 清单 1. 启用 JMX 监控的 JVM 参数
一些教程建议创建一个全局 JAVA_OPTS 环境变量来保存 JMX 标志。其他教程则建议在命令行输入标志:java -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=localhost someExampleClass。 两种方法都是可行的,但是对生产环境而言它们都不是最好的。我发现最好的方法是在服务器的启动脚本中设置这些值。如果每次重新启动服务器时都要输入这些复杂的标志,则表明这是一个不好的解决方案。应避免设置 CLASSPATH 和 JAVA_OPTS 等全局变量,原因有两个:在复制服务器(在服务器之间复制一个一致的启动脚本更容易)时增加了不必要的配置步骤,而且它们强制同一机器上的所有 Java 进程共享同一配置。是的,您可以创建一个详细的清单来提醒您这些琐碎的配置细节,但是记录复杂的东西远不如将复杂去掉有效。 对于 UNIX®、Linux® 和 Mac OS X 系统,Grails 启动脚本是 $GRAILS_HOME/bin/grails。编辑这个文件,添加两个 JAVA_OPTS 行,如清单 2 所示: 清单 2. 在 Grails 启动脚本中为 UNIX、Linux 和 Mac OS X 启用 JMX 监控
对于 Windows®,Grails 启动脚本是 $GRAILS_HOME/bin/grails.bat。在调用 startGrails.bat 之前,向 grails.bat 添加两行,如清单 3 所示: 清单 3. 在 Grails 中为 Windows 启用 JMX 监控
在两个脚本中,注意第一个 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 代理以接受远程连接,需要向 JVM 传递另外几个与 JMX 相关的标志。这几个标志打开一个管理端口并配置安全设置(或本例中的 lack thereof)。 向 Grails 启动脚本添加三个新行,如清单 4 所示: 清单 4. 在 Grails 启动脚本中启用远程 JMX 监控
使用这些设置重新启动 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 基础设施
如果想确保基本配置是正确的,那么现在可重新启动 Grails,但只解决问题的一半:您有了一台 MBean 服务器,但是没有任何 MBean。此时看到的两个 bean(mbeanServer 和 exporter)是需要注册 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 设置
启动 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 服务器
重新启动 Grails,然后重新启动 JConsole。如果连接到端口 9004 上的 localhost,新的 log4j MBean 应该显示在 MBeans 选项卡中。展开 log4j 树元素,单击默认值,然后单击 Info 选项卡。从刚添加到 resources.xml(参见图 5)的条目中,可以看到配置片段: 图 6. 查看默认 MBean 信息 现在可以通过 JMX 看到 log4j 了,下一步是调整一些登录设置。 动态更改 log4j 设置 假设现在 Grails 应用程序表现异常。您应该查找问题的根源。查看 grails-app/conf/Config.groovy,您会发现根登录程序将它的输出发送到控制台,但过滤器被设置为 error — rootLogger="error,stdout"。您希望将登录级别更改为 trace 来提高控制台的输出量。 看一下 JConsole。在 log4j 文件夹下,您应该可以看到根 MBean。可以看到优先级属性被设置为 ERROR,就像在 Config.groovy 中一样。双击 ERROR 值并输入 TRACE,如图 6 所示: 图 7. 将根登录程序优先级从 ERROR 更改为 TRACE 为了验证控制台比以前更好用,在浏览器中,在 Grails 应用程序的主页上单击到 AirportMappingController 的链接。在大量新的输出中,您应该可以找到一些有关 Grails 如何导入初始列表的详细信息。请参阅清单 8 中的样例: 清单 8. 增加 log4j 输出
更改 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 的控制台输出
现在您可以看到输出,以下是详细过程:%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 输出
花一点时间查看 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
如您所见,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
输入 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 代理
如果 Grails 正在运行,应该可以看到如清单 14 所示的输出: 清单 14. testJmx.groovy 脚本的输出
这个脚本中有趣的部分来自 javax.management.MBeanServer,javax.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
有了一个到 MBean 服务器的连接并知道 MBean 的名称后,使用一行即可获取一个新的 GroovyMBean。清单 17 显示了脚本输出: 清单 17. GroovyMBean 输出
是否还记得本文前面提到的 InputArguments?它们是传递给 JVM 的所有 -D 参数。使用这些参数来确认您确实连接到了远程 JMX 代理。再添加两行代码(如清单 18 所示)以输出 String[]: 清单 18. 从运行时 MBean 获取 InputArguments
如果看到清单 19 所示的输出,就说明您圆满完成了所有任务: 清单 19. 显示 InputArguments
有关 GroovyMBean 的更多信息,请参阅 参考资料。 结束语 Grails 是已经可以在企业中使用。常用企业库,比如 JMX、Spring 和 log4j 都可以在 Grails 中使用,因为虽然表面不像,但您仍然是进行传统的 Java EE 开发。 本文是 Trip Planner 应用程序专栏今年的最后一篇文章。我希望保持这系列文章的主题的一致性,以重点讲解核心 Grails 功能。在明年的专栏中,我将继续保留这种风格,但同时我还想纳入各种 Grails 应用程序,以扩大视野。 例如,下个月我将介绍一种新的博客系统。您学习关于如何启动新的 Grails 应用程序的新技术,但它绝不是 “新瓶装旧酒”。您将重温 Grails 的 RESTful,但是是在设置完全 Atom 基础设施的环境中。您将再次使用 JSON 和 Ajax,??过这次是启用日程表和标志云。几个月之后,我将使用另一种新思维。 Grails 继续获得了每个新 Web 站点的大力支持。成熟 Web 框架的标志是能够以多种方式使用它。明年的精通 Grails 文章将演示 Grails 中可能实现的各种 Web 站点。到那时,您将享受到精通 Grails 带来的乐趣。 (责任编辑:A6) cnnew1_cnnew1@" |
对于 Windows®,Grails 启动脚本是 $GRAILS_HOME/bin/grails.bat。在调用 startGrails.bat 之前,向 grails.bat 添加两行,如清单 3 所示:
清单 3. 在 Grails 中为 Windows 启用 JMX 监控
___FCKpd___2 |
在两个脚本中,注意第一个 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 代理以接受远程连接,需要向 JVM 传递另外几个与 JMX 相关的标志。这几个标志打开一个管理端口并配置安全设置(或本例中的 lack thereof)。
向 Grails 启动脚本添加三个新行,如清单 4 所示:
清单 4. 在 Grails 启动脚本中启用远程 JMX 监控
___FCKpd___3 |
使用这些设置重新启动 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 基础设施
___FCKpd___4 |
如果想确保基本配置是正确的,那么现在可重新启动 Grails,但只解决问题的一半:您有了一台 MBean 服务器,但是没有任何 MBean。此时看到的两个 bean(mbeanServer 和 exporter)是需要注册 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 设置
___FCKpd___5 |
启动 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 服务器
___FCKpd___6 |
重新启动 Grails,然后重新启动 JConsole。如果连接到端口 9004 上的 localhost,新的 log4j MBean 应该显示在 MBeans 选项卡中。展开 log4j 树元素,单击默认值,然后单击 Info 选项卡。从刚添加到 resources.xml(参见图 5)的条目中,可以看到配置片段:
图 6. 查看默认 MBean 信息
现在可以通过 JMX 看到 log4j 了,下一步是调整一些登录设置。
动态更改 log4j 设置
假设现在 Grails 应用程序表现异常。您应该查找问题的根源。查看 grails-app/conf/Config.groovy,您会发现根登录程序将它的输出发送到控制台,但过滤器被设置为 error — rootLogger="error,stdout"。您希望将登录级别更改为 trace 来提高控制台的输出量。
看一下 JConsole。在 log4j 文件夹下,您应该可以看到根 MBean。可以看到优先级属性被设置为 ERROR,就像在 Config.groovy 中一样。双击 ERROR 值并输入 TRACE,如图 6 所示:
图 7. 将根登录程序优先级从 ERROR 更改为 TRACE
为了验证控制台比以前更好用,在浏览器中,在 Grails 应用程序的主页上单击到 AirportMappingController 的链接。在大量新的输出中,您应该可以找到一些有关 Grails 如何导入初始列表的详细信息。请参阅清单 8 中的样例:
清单 8. 增加 log4j 输出
___FCKpd___7 |
|
更改 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 的控制台输出
___FCKpd___8 |
现在您可以看到输出,以下是详细过程:%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 输出
___FCKpd___9 |
花一点时间查看 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
___FCKpd___10 |
如您所见,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
___FCKpd___11 |
输入 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 代理
___FCKpd___12 |
如果 Grails 正在运行,应该可以看到如清单 14 所示的输出:
清单 14. testJmx.groovy 脚本的输出
___FCKpd___13 |
|
这个脚本中有趣的部分来自 javax.management.MBeanServer,javax.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
___FCKpd___15 |
有了一个到 MBean 服务器的连接并知道 MBean 的名称后,使用一行即可获取一个新的 GroovyMBean。清单 17 显示了脚本输出:
清单 17. GroovyMBean 输出
___FCKpd___16 |
是否还记得本文前面提到的 InputArguments?它们是传递给 JVM 的所有 -D 参数。使用这些参数来确认您确实连接到了远程 JMX 代理。再添加两行代码(如清单 18 所示)以输出 String[]:
清单 18. 从运行时 MBean 获取 InputArguments
___FCKpd___17 |
如果看到清单 19 所示的输出,就说明您圆满完成了所有任务:
清单 19. 显示 InputArguments
___FCKpd___18 |
有关 GroovyMBean 的更多信息,请参阅 参考资料。
结束语
Grails 是已经可以在企业中使用。常用企业库,比如 JMX、Spring 和 log4j 都可以在 Grails 中使用,因为虽然表面不像,但您仍然是进行传统的 Java EE 开发。
本文是 Trip Planner 应用程序专栏今年的最后一篇文章。我希望保持这系列文章的主题的一致性,以重点讲解核心 Grails 功能。在明年的专栏中,我将继续保留这种风格,但同时我还想纳入各种 Grails 应用程序,以扩大视野。
例如,下个月我将介绍一种新的博客系统。您学习关于如何启动新的 Grails 应用程序的新技术,但它绝不是 “新瓶装旧酒”。您将重温 Grails 的 RESTful,但是是在设置完全 Atom 基础设施的环境中。您将再次使用 JSON 和 Ajax,??过这次是启用日程表和标志云。几个月之后,我将使用另一种新思维。
Grails 继续获得了每个新 Web 站点的大力支持。成熟 Web 框架的标志是能够以多种方式使用它。明年的精通 Grails 文章将演示 Grails 中可能实现的各种 Web 站点。到那时,您将享受到精通 Grails 带来的乐趣。 (责任编辑:A6)