监控是系统管理中的重要环节。系统管理员的日常工作就包括:观察服务器的运行细节,获取统计数据,或者重新配置应用的某些内容。
注意:该配置只适用于需用远程监控 Tomcat 的情况,使用同样的用户在本地监控 Tomcat 则不需要这么配置。
Oracle 的网站上介绍了如何在 Java 6 上配置 JMX 远程:http://docs.oracle.com/javase/6/docs/technotes/guides/management/agent.html。
下面是在 Java 6 上的快速配置向导:
将下列参数添加到 Tomcat 的 setenv.bat
脚本(具体详细信息请查看 RUNNING.txt)。
注意:该语法格式适用于 Windows 系统。命令行只能写在同一行中,包装起来更具可读性。如果 Tomcat 以 Windows 服务的形式运行,使用它的系统配置对话设置该服务的 java 选项。对于 UN\*X
系统来说,要将命令行开头的 "set "
去掉。
set CATALINA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=%my.jmx.port% -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
1.如果需要授权,则添加并修改下列命令:
-Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password -Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access
2.编辑访问权限文件 $CATALINA_BASE/conf/jmxremote.access
:
monitorRole readonly controlRole readwrite
3.编辑密码文件 $CATALINA_BASE/conf/jmxremote.password
:
monitorRole tomcat controlRole tomcat
技巧:密码文件应该是只读的,并且只能被运行 Tomcat 的操作系统用户所访问。
注意:JSR 160 JMX 适配器在一个随机端口上打开了第二个数据通道。假如本地安装了防火墙,这就会出现问题。要想解决它,可以按照侦听器文档中介绍的方法,配置一个 JmxRemoteLifecycleListener
。
为了简化 JMX 的用法,加入了一些可能会与 antlib 使用的一系列任务。
antlib:将 catalina-ant.jar 从 $CATALINA_HOME/lib
复制到 $ANT_HOME/lib
。
下面的例子展示了 JMX 存储器的用法。
注意:为了提高可读性,这里将 name
属性值予以包装。它必须写在同一行中,不允许带有空格。
<project name="Catalina Ant JMX" xmlns:jmx="antlib:org.apache.catalina.ant.jmx" default="state" basedir="."> <property name="jmx.server.name" value="localhost" /> <property name="jmx.server.port" value="9012" /> <property name="cluster.server.address" value="192.168.1.75" /> <property name="cluster.server.port" value="9025" /> <target name="state" description="Show JMX Cluster state"> <jmx:open host="${jmx.server.name}" port="${jmx.server.port}" username="controlRole" password="tomcat"/> <jmx:get name= "Catalina:type=IDataSender,host=localhost, senderAddress=${cluster.server.address},senderPort=${cluster.server.port}" attribute="connected" resultproperty="IDataSender.backup.connected" echo="false" /> <jmx:get name="Catalina:type=ClusterSender,host=localhost" attribute="senderObjectNames" resultproperty="senderObjectNames" echo="false" /> <!-- get current maxActiveSession from ClusterTest application echo it to Ant output and store at property <em>clustertest.maxActiveSessions.orginal</em> --> <jmx:get name="Catalina:type=Manager,context=/ClusterTest,host=localhost" attribute="maxActiveSessions" resultproperty="clustertest.maxActiveSessions.orginal" echo="true" /> <!-- set maxActiveSession to 100 --> <jmx:set name="Catalina:type=Manager,context=/ClusterTest,host=localhost" attribute="maxActiveSessions" value="100" type="int" /> <!-- get all sessions and split result as delimiter <em>SPACE</em> for easy access all session ids directly with Ant property sessions.[0..n]. --> <jmx:invoke name="Catalina:type=Manager,context=/ClusterTest,host=localhost" operation="listSessionIds" resultproperty="sessions" echo="false" delimiter=" " /> <!-- Access session attribute <em>Hello</em> from first session. --> <jmx:invoke name="Catalina:type=Manager,context=/ClusterTest,host=localhost" operation="getSessionAttribute" resultproperty="Hello" echo="false" > <arg value="${sessions.0}"/> <arg value="Hello"/> </jmx:invoke> <!-- Query for all application manager.of the server from all hosts and bind all attributes from all found manager MBeans. --> <jmx:query name="Catalina:type=Manager,*" resultproperty="manager" echo="true" attributebinding="true" /> <!-- echo the create properties --> <echo> senderObjectNames: ${senderObjectNames.0} IDataSender.backup.connected: ${IDataSender.backup.connected} session: ${sessions.0} manager.length: ${manager.length} manager.0.name: ${manager.0.name} manager.1.name: ${manager.1.name} hello: ${Hello} manager.ClusterTest.0.name: ${manager.ClusterTest.0.name} manager.ClusterTest.0.activeSessions: ${manager.ClusterTest.0.activeSessions} manager.ClusterTest.0.counterSend_EVT_SESSION_EXPIRED: ${manager.ClusterTest.0.counterSend_EVT_SESSION_EXPIRED} manager.ClusterTest.0.counterSend_EVT_GET_ALL_SESSIONS: ${manager.ClusterTest.0.counterSend_EVT_GET_ALL_SESSIONS} </echo> </target> </project>
导入:利用 <import file="${CATALINA.HOME}/bin/catalina-tasks.xml" />
导入 JMX 存取器项目,利用 jmxOpen、jmxSet、jmxGet、jmxQuery、jmxInvoke、jmxEquals 和 jmxCondition 来引用任务。
属性列表
属性 | 描述 | 默认值 |
---|---|---|
url | 设定 JMX 连接 URL——service:jmx:rmi:///jndi/rmi://localhost:8050/jmxrmi |
- |
host | 设定主机,缩短长的 URL 格式 | localhost |
port | 设定远程连接端口 | 8050 |
username | 远程 JMX 连接用户名 | - |
password | 远程 JMX 连接密码 | - |
ref | 内部连接引用的名称。利用该属性,在同一个 Ant 项目中配置不止一个连接 | jmx.server |
echo | Echo 命令用途(用于访问分析或调试) | false |
if | 只有当指定名称的属性存在于当前项目时才执行 | - |
unless | 只有当指定名称的属性不存在于当前项目时才执行 | - |
打开新的 JMX 连接的范例如下:
<jmx:open host="${jmx.server.name}" port="${jmx.server.port}" />
打开指定 URL 的 JMX 连接的范例,带有授权并存储在其他引用中:
<jmx:open url="service:jmx:rmi:///jndi/rmi://localhost:9024/jmxrmi" ref="jmx.server.9024" username="controlRole" password="tomcat" />
打开指定 URL 的 JMX 连接的范例,带有授权并存储在其他引用中,但是必须要求 jmx.if 属性存在,而 jmx.unless 属性不存在。
<jmx:open url="service:jmx:rmi:///jndi/rmi://localhost:9024/jmxrmi" ref="jmx.server.9024" username="controlRole" password="tomcat" if="jmx.if" unless="jmx.unless" />
注意:jmxOpen 任务中所有属性也存在于其他所有任务和条件中。
属性列表
属性 | 描述 | 默认值 |
---|---|---|
name |
完全限定的 JMX ObjectName——Catalina:type=Server | - |
attribute |
已有的 MBean 属性(参看上文介绍的 Tomcat MBean 描述) | - |
ref |
JMX 连接引用 | jmx.server |
echo |
Echo 命令用途(访问与结果) | false |
resultproperty |
在该项目属性中保存结果 | - |
delimiter |
用分隔符(java.util.StringTokenizier )分隔结果,使用 resultproperty 作为前缀来保存令牌 |
- |
separatearrayresults |
返回值为数组时,将结果保存为属性列表($resultproperty.[0..N] 和 $resultproperty.length) | true |
从默认的 JMX 连接中获取远程 MBean 属性:
<jmx:get name="Catalina:type=Manager,context=/servlets-examples,host=localhost" attribute="maxActiveSessions" resultproperty="servlets-examples.maxActiveSessions" />
获取结果数组,并将其分隔成独立的一些属性:
<jmx:get name="Catalina:type=ClusterSender,host=localhost" attribute="senderObjectNames" resultproperty="senderObjectNames" />
访问 senderObjectNames 属性:
${senderObjectNames.length} give the number of returned sender list. ${senderObjectNames.[0..N]} found all sender object names
获取连接的 IDataSender 属性(只有在配置了集群时)。
注意:这里为了可读性,将 name
属性加以包装。代码应该位于同一行中,并且不含有空格。
<jmx:query failonerror="false" name="Catalina:type=Cluster,host=${tomcat.application.host}" resultproperty="cluster" /> <jmx:get name= "Catalina:type=IDataSender,host=${tomcat.application.host}, senderAddress=${cluster.backup.address},senderPort=${cluster.backup.port}" attribute="connected" resultproperty="datasender.connected" if="cluster.0.name" />
属性列表
属性 | 说明 | 默认值 |
---|---|---|
name |
完全限定的 JMX ObjectName——Catalina:type=Server |
- |
attribute |
已有的 MBean 属性(详情参见上文介绍的 Tomcat MBean 说明) | - |
value |
设定为属性的值 | - |
type |
属性类型 | java.lang.String |
ref |
JMX 连接引用 | jmx.server |
echo |
Echo 命令用途(访问与结果) | false |
设定远程 MBean 属性值的范例如下:
<jmx:set name="Catalina:type=Manager,context=/servlets-examples,host=localhost" attribute="maxActiveSessions" value="500" type="int" />
属性列表
属性 | 说明 | 默认值 | |
---|---|---|---|
name |
完全限定的 JMX ObjectName——Catalina:type=Server |
- | |
operation |
已有的 MBean 操作(funcspecs/fs-admin-opers.html) | - | |
ref |
JMX 连接引用 | jmx.server |
jmx.server |
echo |
Echo 命令用途(访问与结果) | false |
|
resultproperty |
在这一项目属性中保存结果 | - | |
delimiter |
用分隔符(java.util.StringTokenizier )分隔结果,使用 resultproperty 作为前缀来保存令牌 |
- | |
separatearrayresults |
返回值为数组时,将结果保存为属性列表($resultproperty.[0..N] 和 $resultproperty.length) | true |
停止应用:
<jmx:invoke name="Catalina:type=Manager,context=/servlets-examples,host=localhost" operation="stop"/>
可以在 ${sessions.[0..N} 属性中找到 sessionid,然后利用 ${sessions.length}
属性来访问计数。
获取所有 sessionid 的范例如下:
<jmx:invoke name="Catalina:type=Manager,context=/servlets-examples,host=localhost" operation="listSessionIds" resultproperty="sessions" delimiter=" " />
现在你可以在 ${sessions.[0..N} 属性中找到 sessionid,然后利用 ${sessions.length}
属性来访问计数。
从 ${sessionid.0} 会话中获取远程 MBean 会话属性:
<jmx:invoke name="Catalina:type=Manager,context=/ClusterTest,host=localhost" operation="getSessionAttribute" resultproperty="hello"> <arg value="${sessionid.0}"/> <arg value="Hello" /> </jmx:invoke>
在虚拟主机 localhost 上创建新的访问日志记录器值:
<jmx:invoke name="Catalina:type=MBeanFactory" operation="createAccessLoggerValve" resultproperty="accessLoggerObjectName" > <arg value="Catalina:type=Host,host=localhost"/> </jmx:invoke>
现在可以利用 ${accessLoggerObjectName} 属性上存储的名称找到新的 MBean 了。
属性列表:
属性 | 描述 | 默认值 |
---|---|---|
name |
JMX ObjectName 查询字符串——Catalina:type=Manager,* | - |
ref |
JMX 连接引用 | jmx.server |
echo |
Echo 命令用途(访问及结果) | false |
resultproperty |
将项目属性名做为前缀加到所有已建立的 MBean 上(mbeans.[0..N].objectname ) |
- |
attributebinduing |
除了 name 之外,绑定所有的 MBean 属性 |
false |
delimiter |
用分隔符(java.util.StringTokenizier )分隔结果,使用 resultproperty 作为前缀来保存令牌 |
- |
separatearrayresults |
返回值为数组时,将结果保存为属性列表($resultproperty.[0..N] 和 $resultproperty.length) | true |
从所有的服务及主机中获取所有的 Manager ObjectName:
<jmx:query name="Catalina:type=Manager,* resultproperty="manager" />
现在,在 ${manager.[0..N].name} 属性上可以找到 Session Manager,利用 ${manager.length} 属性来访问结果对象计数器。
从 servlet-examples 程序中获取 Manager,并绑定所有的 MBean 属性:
<jmx:query name="Catalina:type=Manager,context=/servlet-examples,host=localhost*" attributebinding="true" resultproperty="manager.servletExamples" />
现在我们可以在 ${manager.servletExamples.0.name} 属性中找到 manager,并利用 ${manager.servletExamples.0.[manager attribute names]} 访问该 manager 的所有属性。MBean 的结果对象计数器被保存在 ${manager.length} 属性中。
在下面范例中,从服务器中获取所有的 MBean,并保存在外部的 XML 属性文件中。
<project name="jmx.query" xmlns:jmx="antlib:org.apache.catalina.ant.jmx" default="query-all" basedir="."> <property name="jmx.host" value="localhost"/> <property name="jmx.port" value="8050"/> <property name="jmx.username" value="controlRole"/> <property name="jmx.password" value="tomcat"/> <target name="query-all" description="Query all MBeans of a server"> <!-- Configure connection --> <jmx:open host="${jmx.host}" port="${jmx.port}" ref="jmx.server" username="${jmx.username}" password="${jmx.password}"/> <!-- Query MBean list --> <jmx:query name="*:*" resultproperty="mbeans" attributebinding="false"/> <echoproperties destfile="mbeans.properties" prefix="mbeans." format="xml"/> <!-- Print results --> <echo message= "Number of MBeans in server ${jmx.host}:${jmx.port} is ${mbeans.length}"/> </target> </project>
现在就可以在 mbeans.properties 文件中找到所有的 MBean 了。
属性列表
属性 | 描述 | 默认值 |
---|---|---|
name |
完全限定的 JMX ObjectName——Catalina:type=MBeanFactory | - |
className |
已有的 MBean 完全限定的类名(参见上文的 Tomcat MBean 说明) | - |
classLoader |
服务器或 Web 应用类加载器的 ObjectName (Catalina:type=ServerClassLoader,name=[server,common,shared] 或 Catalina:type=WebappClassLoader,context=/myapps,host=localhost) |
- |
ref |
JMX 连接引用 | jmx.server |
echo |
Echo 命令用途(访问及结果) | false |
创建远程 MBean 的范例如下:
<jmx:create ref="${jmx.reference}" name="Catalina:type=MBeanFactory" className="org.apache.commons.modeler.BaseModelMBean" classLoader="Catalina:type=ServerClassLoader,name=server"> <arg value="org.apache.catalina.mbeans.MBeanFactory" /> </jmx:create>
警告:许多 Tomcat MBean 一经创建就没有与父级连接。Valve、集群以及Realm 的 MBean 都不会自动与父级相连。作为替代,可以使用 MBeanFactory 来创建操作。
属性列表
属性 | 描述 | 默认值 |
---|---|---|
name |
完全限定的 JMX ObjectName——Catalina:type=MBeanFactory | = |
ref |
JMX 连接引用 | jmx.server |
echo |
Echo 命令使用(访问及结果) | false |
注销远程 MBean 范例如下:
<jmx:unregister name="Catalina:type=MBeanFactory" />
警告:许多 Tomcat MBean 都无法注销。MBean 无法从其父级脱离。可以使用 MBeanFactory 来移除操作。
属性列表
属性 | 描述 | 默认值 |
---|---|---|
url |
设定 JMX 连接 URL ——service:jmx:rmi:///jndi/rmi://localhost:8050/jmxrmi | - |
host |
设定主机,将非常长的 URL 格式予以缩短 | localhost |
port |
设定远程连接端口 | 8050 |
username |
远程 JMX 连接用户名 | - |
password |
远程 JMX 连接密码 | - |
ref |
内部连接引用名称。利用这一属性,可以在同一个 Ant 项目中配置多个连接。 | jmx.server |
name |
完全限定的 JMX ObjectName——Catalina:type=Server | - |
echo |
Echo 命令使用(访问及结果) | false |
if |
只有当给定名称的属性存在于当前项目中才执行 | - |
unless |
只有当给定名称的属性不存在于当前项目中才执行 | - |
value (必须) |
操作的第二个参数 | - |
type |
表达操作的值类型(支持 long 和 double) | long |
operation |
提供以下操作== 等于!= 不等于> 大于(> )>= 大于或等于(>= )< 小于(< )<= 小于或等于(<= ) |
== |
等待服务器连接,集群备份节点可访问。
<target name="wait"> <waitfor maxwait="${maxwait}" maxwaitunit="second" timeoutproperty="server.timeout" > <and> <socket server="${server.name}" port="${server.port}"/> <http url="${url}"/> <jmx:condition operation="==" host="localhost" port="9014" username="controlRole" password="tomcat" name= "Catalina:type=IDataSender,host=localhost,senderAddress=192.168.111.1,senderPort=9025" attribute="connected" value="true" /> </and> </waitfor> <fail if="server.timeout" message="Server ${url} don't answer inside ${maxwait} sec" /> <echo message="Server ${url} alive" /> </target>
属性列表
属性 | 描述 | 默认值 |
---|---|---|
url |
设定 JMX 连接 URL ——service:jmx:rmi:///jndi/rmi://localhost:8050/jmxrmi | - |
host |
设定主机,将非常长的 URL 格式予以缩短 | localhost |
port |
设定远程连接端口 | 8050 |
username |
远程 JMX 连接用户名 | - |
password |
远程 JMX 连接密码 | - |
ref |
内部连接引用名称。利用这一属性,可以在同一个 Ant 项目中配置多个连接。 | jmx.server |
name |
完全限定的 JMX ObjectName——Catalina:type=Server | - |
echo |
Echo 命令使用(访问及结果) | false |
等待服务器连接,集群备份节点可访问。
<target name="wait"> <waitfor maxwait="${maxwait}" maxwaitunit="second" timeoutproperty="server.timeout" > <and> <socket server="${server.name}" port="${server.port}"/> <http url="${url}"/> <jmx:equals host="localhost" port="9014" username="controlRole" password="tomcat" name= "Catalina:type=IDataSender,host=localhost,senderAddress=192.168.111.1,senderPort=9025" attribute="connected" value="true" /> </and> </waitfor> <fail if="server.timeout" message="Server ${url} don't answer inside ${maxwait} sec" /> <echo message="Server ${url} alive" /> </target>
Tomcat 为使用远程(或者甚至本地的)JMX 连接提供了一个替代方案:Tomcat 的 JMXProxyServlet,但它仍能让你访问 JMX 所提供的任何内容。
JMXProxyServlet 允许客户端通过 HTTP 接口来发送 JMX 查询。相比直接从客户端程序使用 JMX 来说,该技术具有以下优势:
常见的服务器监控软件(比如 Nagios 或 Ichinga)中都存在过度使用 JMX 的问题:如果想通过 JMX 监控 10 项,就必须启动 10 个 JVM,保持 10 个 JMX 连接,每过几分钟就要将它们全部关闭。有了 JMXProxyServlet,利用 10 个 HTTP 连接就能搞定了。
关于 JMXProxyServlet 的详细说明,可查阅 Tomcat Manager。