JMXTerm:交互式和脚本化





5.00/5 (1投票)
如何使用 JMXTerm 的基本功能,并通过脚本化扩展其用途。
引言
JMXTerm 是一个命令行工具,它实现了 JConsole 的功能。在脚本化环境中使其正常工作需要一些技巧。在编写脚本之前,您需要以交互模式启动 JMXTerm,并找出检索所需信息所需的命令。
默认情况下,Java 应用程序不太可能启用 JMXRemote 以允许通过网络进行访问,但我们可以直接连接到 Java 进程并使用进程 ID 本地访问 JMX MBeans。如果您愿意,可以使用 netstat -tulpn 命令查找该 PID。下面的屏幕截图显示了我通过知道服务端口 8080 并识别其为 java 来确定的进程 ID。您也可以使用 JMXTerm 来识别进程 ID。
交互式 JMXTerm
启动 JMXTerm 很简单,唯一的先决条件就是……您猜对了……Java。使用 Java 的 -jar 选项启动它。在下面的示例中,我将分解我用来从 MBean 中获取数据的步骤。在下面的会话中,蓝色粗体字符是我输入的。
从命令提示符启动 JMXTerm
[ec2-user@ip-172-31-4-10 ~]$ java -jar jmxterm-1.0-alpha-4-uber.jar
启动后,您将看到 JMXTerm 提示符 "$>"。键入 "help" 以获取命令列表。
$>help #following commands are available to use:
about - Display about page
bean - Display or set current selected MBean.
beans - List available beans under a domain or all domains
bye - Terminate console and exit
close - Close current JMX connection
domain - Display or set current selected domain.
domains - List all available domain names
exit - Terminate console and exit
get - Get value of MBean attribute(s)
help - Display available commands or usage of a command
info - Display detail information about an MBean
jvms - List all running local JVM processes
open - Open JMX session or display current connection
option - Set options for command session
quit - Terminate console and exit
run - Invoke an MBean operation
set - Set value of an MBean attribute
我们需要做的第一件事是连接到一个 JMX 实例。如引言中所述,我们可以在启动 JMXTerm 之前获取该信息,但我们也可以使用 jvms 命令从程序中找到它。
$>jvms 7160 (m) - start.jar jetty.state=/data/jettyapps/chef-cms-app/jetty.state jetty-logging.xml jetty-started.xml start-log-file=/data/jettyapps/chef-cms-app/logs/start.log 15925 ( ) - jmxterm-1.0-alpha-4-uber.jar
所以我们看到 PID 是响应行上的第一个项目,7160,我们看到这是 chef-cms-app。接下来,我们打开该进程。
$>open 7160 #Connection to 7160 is opened
好的……我们已经打开了一个连接。我们需要知道我们有什么可以操作的。使用 "beans" 命令获取可用 bean 的列表。(下面的列表已缩短,因为它非常长)。
$>beans #domain = JMImplementation: JMImplementation:type=MBeanServerDelegate #domain = cms-app: cms-app:service=AuditLogEventService,type=service cms-app:service=AuthorService,type=service ... cms-app:type=utility,utility=AuditLogEventController … #domain = com.amazonaws.management: com.amazonaws.management:type=AwsSdkMetrics #domain = com.sun.management: com.sun.management:type=HotSpotDiagnostic …
从 beans 命令的输出中,我们看到有很多 bean,但我们注意到它们是按域分组的。我们可以看到这里我们关心的域是 cms-app。我们需要选择该域。
$>domain cms-app #domain is set to cms-app
如果您再次键入 "beans" 命令,列表将只返回 cms-app 中的 bean。bean 名称有点难输入,但 JMXTerm 允许复制和粘贴(突出显示复制,右键单击粘贴),因此我们可以选择一个特定的 bean。注意!因为我们已经选择了 cms-app 域,所以我们在选择 bean 时不需要 cms-app 前缀!我们在上面看到了 cms-app, service=AuditLogEventService,type=service
中的第一个 bean。让我们使用 bean(不是 beans)命令设置该 bean。注意在此级别使用完整 bean 名称时会出错。
$>bean cms-app:service=AuditLogEventService,type=service #IllegalArgumentException: Bean name cms-app:service=AuditLogEventService,type=service isn't valid $>bean service=AuditLogEventService,type=service #bean is set to cms-app:service=AuditLogEventService,type=service
现在我们已经选择了域和 bean。我们可以使用 info 命令获取此 bean 提供的属性和操作(以及可用的通知)列表。
$>info #mbean = cms-app:service=AuditLogEventService,type=service #class name = com.samsung.cms.AuditLogEventService # attributes %0 - Expose (java.lang.Object, w) %1 - LastEventId (java.lang.Object, r) %2 - MetaClass (groovy.lang.MetaClass, rw) %3 - TransactionManager (org.springframework.transaction.PlatformTransactionManager, rw) # operations %0 - java.lang.Object getExpose() %1 - java.lang.Object getLastEventId() %2 - org.springframework.transaction.PlatformTransactionManager getTransactionManager() %3 - void setExpose(java.lang.Object p1) %4 - void setTransactionManager(org.springframework.transaction.PlatformTransactionManager p1) #there's no notifications
有了属性和操作的列表,我们现在就可以与 bean 进行交互了。假设我们要查看 LastEventID
的值,我们将使用 "get" 命令来获取它。
$>get LastEventId #mbean = cms-app:service=AuditLogEventService,type=service: LastEventId = 66251;
我们可以对其他 bean 重复此过程,并且可以使用 SET 命令更改值(如果 bean 允许)。
所以,我们知道如何手动从 bean 中获取值,但如果我们想自动化这个过程呢?
脚本化 JMXTerm
脚本化 JMXTerm 可以通过多种方式完成。您可以将参数传递给 JMXTerm 命令行或通过管道,但我不喜欢为每个请求重复调用它。也就是说,JMXTerm 的一个选项 "-i" 允许指定一个包含要执行的命令列表的输入命令文件。遗憾的是,当我们尝试将进程 ID 管道传输到 JMXTerm 时,此选项不起作用,这意味着如果我们想避免 JMX Remoting,就需要付出额外的努力。我选择使用 .jmx 扩展名来标识 JMXTerm 脚本。
只要 Jcom.jmx 包含打开正确进程 ID 的命令,此命令就可以执行 Jcom.jmx JMXTerm 脚本。
/usr/bin/java -jar jmxterm-1.0-alpha-4-uber.jar -n -v silent -i /home/ec2-user/Jcom.jmx
Jcom.jmx 文件的内容反映了在 JMXTerm 会话中输入的命令。脚本文件缺少由我的脚本预先添加的第一行。如果您想执行脚本本身,则必须以 "open 7160" 开头,其中 7160 是您要访问的进程的 ID。
------A Jcom.jmx File #Set the domain for Chef App domain cms-app #Set the first service with accessible attribute bean service=AuditLogEventService,type=service get LastEventId get LastEventId
由于 JMX 主进程的进程 ID 会更改,因此我使用一个脚本来启动 JMXTerm 并创建一个名为 Jcom.jmx.tmp 的修改后的 JCom.jmx,然后将其用作实际输入文件,其中包含缺失的第一行。
-----The modified Jcom.jmx.tmp file. open 7160 #Set the domain for Chef App domain cms-app #Set the first service with accessible attribute bean service=AuditLogEventService,type=service get LastEventId get LastEventId
// My bash launcher for JMXTerm
#This script attempts to find the process ID of the JMX process, then dump bean info using Jcom.jmx file if [ "$1" != "" ]; then #If appname passed on commandline, bring up that process ID AppName=$1 PID=`echo "jvms" | /usr/bin/java -jar jmxterm-1.0-alpha-4-uber.jar -n -v silent | grep $AppName | awk '{print $1;}'` else #Try to bring up a local JVM with the "(m)" (management flag?) set. AppName="Unspecified JWM" PID=`echo "jvms" | /usr/bin/java -jar jmxterm-1.0-alpha-4-uber.jar -n -v silent | grep "(m)" | awk '{print $1;}'` fi #Check if we have multiple results echo -n "$PID" | while IFS= read -N 1 a; do if [[ "$a" == $'\n' ]] ; then echo "Multiple PIDs found for $AppName" echo "$PID" echo "Please specify a unique application identifier" exit 10 fi done if [ $? -eq 10 ]; then exit 0; fi if [ "$PID" == "" ]; then #If we didnt get a result echo "Unable to determine unique process id for $AppName" exit fi echo "Using port $PID for $AppName" #The Process ID will change, but we dont want to change our JMXTerm Script. #Create a temporary script with the open command for the process id, and the other commands. echo "open $PID" > /home/ec2-user/Jcom.jmx.tmp cat /home/ec2-user/Jcom.jmx >> Jcom.jmx.tmp #Call JMXTerm, and pass it the combined command file. Result will hold what is returned. RESULT=`/usr/bin/java -jar jmxterm-1.0-alpha-4-uber.jar -n -v silent -i /home/ec2-user/Jcom.jmx.tmp` echo "Results:" echo "$RESULT" #Remove the temp file to keep things clean. rm /home/ec2-user/Jcom.jmx.tmp
JMXRemote
如果您想远程访问 JMX MBeans,则需要使用适当的标志启动 Java 会话以启用远程访问。最简单的方法是禁用安全,这样就不需要 ssl 和身份验证。此解决方案在 AWS 环境中的可用性有限,因为 JMX 不仅使用指定的端口,还会打开几个其他随机端口。这使得配置安全组和 NACL 变得不合理地复杂,如果您的资源分布广泛。
下面的示例启动了我创建的一个测试 jar,该 jar 在端口 9999 上实现 JMX,并禁用了安全,以允许 JMXTerm 进行远程连接。
java -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar /home/ec2-user/SystemConfigMBean.jar
一旦您启动了会话并在网络上可用,您就可以使用 hostname:port
而不是进程 ID 来连接 JMXTerm。