65.9K
CodeProject 正在变化。 阅读更多。
Home

JMXTerm:交互式和脚本化

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2015 年 6 月 7 日

CPOL

5分钟阅读

viewsIcon

32631

如何使用 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。

© . All rights reserved.