监控和管理 Tomcat

目录

简介

监控是系统管理的关键方面。查看正在运行的服务器内部,获取一些统计信息或重新配置应用程序的某些方面都是日常管理任务。

启用 JMX 远程

注意: 只有在您要远程监控 Tomcat 时才需要此配置。如果您要使用与 Tomcat 运行相同的用户在本地监控它,则不需要此配置。

Oracle 网站包含选项列表以及如何在 Java 11 上配置 JMX 远程: http://docs.oracle.com/javase/6/docs/technotes/guides/management/agent.html.

以下是 Java 11 的快速配置指南

将以下参数添加到 Tomcat 的 setenv.bat 脚本中(有关详细信息,请参阅 RUNNING.txt)。
注意: 此语法适用于 Microsoft Windows。命令必须在同一行上。它被包装以更易读。如果 Tomcat 作为 Windows 服务运行,请使用其配置对话框为服务设置 java 选项。对于 Linux、MacOS 等,从行首删除 "set "

set CATALINA_OPTS=-Dcom.sun.management.jmxremote.port=%my.jmx.port%
  -Dcom.sun.management.jmxremote.rmi.port=%my.rmi.port%
  -Dcom.sun.management.jmxremote.ssl=false
  -Dcom.sun.management.jmxremote.authenticate=false

如果您没有设置 com.sun.management.jmxremote.rmi.port,则 JSR 160 JMX-Adaptor 将随机选择一个端口,这可能会使配置防火墙以允许访问变得困难。

如果您需要 TLS

  1. 更改并添加此内容
      -Dcom.sun.management.jmxremote.ssl=true
      -Dcom.sun.management.jmxremote.registry.ssl=true
    
  2. 要配置协议和/或密码套件,请使用
      -Dcom.sun.management.jmxremote.ssl.enabled.protocols=%my.jmx.ssl.protocols%
      -Dcom.sun.management.jmxremote.ssl.enabled.cipher.suites=%my.jmx.cipher.suites%
    
  3. 要进行客户端证书身份验证,请使用
      -Dcom.sun.management.jmxremote.ssl.need.client.auth=%my.jmx.ssl.clientauth%

如果您需要授权(强烈建议始终将 TLS 与身份验证一起使用)

  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 运行的系统用户访问。
  4. 或者,您可以使用以下方法配置 JAAS 登录模块:
      -Dcom.sun.management.jmxremote.login.config=%login.module.name%

如果您需要指定要在发送到客户端的 RMI 存根中使用的主机名(例如,因为必须用于连接的公共主机名与本地主机名不同),那么您可以设置

set CATALINA_OPTS=-Djava.rmi.server.hostname

如果您需要为 JMX 服务指定一个特定的接口来绑定,那么您可以设置

set CATALINA_OPTS=-Dcom.sun.management.jmxremote.host

使用 JMX 远程 Ant 任务管理 Tomcat

为了简化 Ant 中 JMX 的使用,提供了一组可与 antlib 一起使用的任务。

antlib:将您的 catalina-ant.jar 从 $CATALINA_HOME/lib 复制到 $ANT_HOME/lib。

以下示例展示了 JMX Accessor 的用法
注意: 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.original</em>
    -->
    <jmx:get
      name="Catalina:type=Manager,context=/ClusterTest,host=localhost"
      attribute="maxActiveSessions"
      resultproperty="clustertest.maxActiveSessions.original"
      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 Accessor 项目,并使用 jmxOpenjmxSetjmxGetjmxQueryjmxInvokejmxEqualsjmxCondition 引用任务。

JMXAccessorOpenTask - JMX 打开连接任务

属性列表

属性 描述 默认值
url 设置 JMX 连接 URL - service:jmx:rmi:///jndi/rmi://127.0.0.1:8050/jmxrmi
host 设置主机,简化非常长的 URL 语法。 localhost
port 设置远程连接端口 8050
username 远程 JMX 连接用户名。
password 远程 JMX 连接密码。
ref 内部连接引用的名称。使用此属性,您可以在同一个 Ant 项目中配置多个连接。 jmx.server
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://127.0.0.1:9024/jmxrmi"
    ref="jmx.server.9024"
    username="controlRole"
    password="tomcat"
  />

从 URL 打开 JMX 连接,并使用授权存储到其他引用的示例,但仅在 jmx.if 属性存在且 jmx.unless 属性不存在时执行

  <jmx:open
    url="service:jmx:rmi:///jndi/rmi://127.0.0.1:9024/jmxrmi"
    ref="jmx.server.9024"
    username="controlRole"
    password="tomcat"
    if="jmx.if"
    unless="jmx.unless"
  />

注意jmxOpen 任务中的所有属性也存在于所有其他任务和条件中。

JMXAccessorGetTask: 获取属性值 Ant 任务

属性列表

属性 描述 默认值
name 完全限定的 JMX ObjectName -- Catalina:type=Server
attribute 现有的 MBean 属性(参见上面的 Tomcat MBean 描述)
ref JMX 连接引用 jmx.server
echo 回显命令用法(访问和结果) false
resultproperty 将结果保存到此项目属性中
delimiter 使用分隔符(java.util.StringTokenizer)分割结果,并使用 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" />

JMXAccessorSetTask: 设置属性值 Ant 任务

属性列表

属性 描述 默认值
name 完全限定的 JMX ObjectName -- Catalina:type=Server
attribute 现有的 MBean 属性(参见上面的 Tomcat MBean 描述)
value 设置为属性的值
type 属性的类型。 java.lang.String
ref JMX 连接引用 jmx.server
echo 回显命令用法(访问和结果) false

设置远程 MBean 属性值的示例

  <jmx:set
    name="Catalina:type=Manager,context=/servlets-examples,host=localhost"
    attribute="maxActiveSessions"
    value="500"
    type="int"
  />

JMXAccessorInvokeTask: 调用 MBean 操作 Ant 任务

属性列表

属性 描述 默认值
name 完全限定的 JMX ObjectName -- Catalina:type=Server
operation 现有的 MBean 操作
ref JMX 连接引用 jmx.server
echo 回显命令用法(访问和结果) false
resultproperty 将结果保存到此项目属性中
delimiter 使用分隔符(java.util.StringTokenizer)分割结果,并使用 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>

在 vhost localhost 上创建新的访问日志记录阀的示例

 <jmx:invoke
         name="Catalina:type=MBeanFactory"
         operation="createAccessLoggerValve"
         resultproperty="accessLoggerObjectName"
 >
     <arg value="Catalina:type=Host,host=localhost"/>
 </jmx:invoke>

现在,您可以在 ${accessLoggerObjectName} 属性中找到新的 MBean。

JMXAccessorQueryTask: 查询 MBean Ant 任务

属性列表

属性 描述 默认值
name JMX ObjectName 查询字符串 - Catalina:type=Manager,*
ref JMX 连接引用 jmx.server
echo 回显命令用法(访问和结果) false
resultproperty 将项目属性名称作为前缀添加到所有找到的 MBean(mbeans.[0..N].objectname
attributebinding 除了 name 之外,还绑定所有 MBean 属性 false
delimiter 使用分隔符(java.util.StringTokenizer)分割结果,并使用 resultproperty 作为前缀存储标记。
separatearrayresults 当返回值为数组时,将结果保存为属性列表($resultproperty.[0..N]$resultproperty.length true

从所有服务和主机获取所有 Manager ObjectNames

  <jmx:query
    name="Catalina:type=Manager,*
    resultproperty="manager" />

现在,您可以在 ${manager.[0..N].name} 属性中找到会话管理器,并使用 ${manager.length} 属性访问结果对象计数器。

servlet-examples 应用程序获取管理器并绑定所有 MBean 属性的示例

  <jmx:query
    name="Catalina:type=Manager,context=/servlet-examples,host=localhost*"
    attributebinding="true"
    resultproperty="manager.servletExamples" />

现在,您可以在 ${manager.servletExamples.0.name} 属性中找到管理器,并可以使用 ${manager.servletExamples.0.[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。

JMXAccessorCreateTask: 远程创建 MBean Ant 任务

属性列表

属性 描述 默认值
name 完全限定的 JMX 对象名称 -- Catalina:type=MBeanFactory
className 现有的 MBean 完全限定类名(参见上面的 Tomcat MBean 描述)
classLoader 服务器或 Web 应用程序类加载器的对象名称
( Catalina:type=ServerClassLoader,name=[server,common,shared]
Catalina:type=WebappClassLoader,context=/myapps,host=localhost)
ref JMX 连接引用 jmx.server
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、Cluster 和 Realm MBean 不会自动
与其父级连接。请改用 MBeanFactory 创建
操作。

JMXAccessorUnregisterTask: 远程注销 MBean Ant 任务

属性列表

属性 描述 默认值
name 完全限定的 JMX 对象名称 -- Catalina:type=MBeanFactory
ref JMX 连接引用 jmx.server
echo 回显命令用法(访问和结果) false

注销远程 MBean 的示例

  <jmx:unregister
    name="Catalina:type=MBeanFactory"
  />

警告:许多 Tomcat MBean 无法注销。
MBean 不会与其父级分离。请改用 MBeanFactory
删除操作。

JMXAccessorCondition: 表达条件

属性列表

属性 描述 默认值
url 设置 JMX 连接 URL - service:jmx:rmi:///jndi/rmi://127.0.0.1:8050/jmxrmi
host 设置主机,简化非常长的 URL 语法。 localhost
port 设置远程连接端口 8050
username 远程 JMX 连接用户名。
password 远程 JMX 连接密码。
ref 内部连接引用的名称。使用此属性,您可以在同一个 Ant 项目中配置多个连接。 jmx.server
name 完全限定的 JMX ObjectName -- Catalina:type=Server
echo 回声条件用法(访问和结果) false
if 仅在当前项目中存在给定名称的属性时执行。
unless 仅在当前项目中不存在给定名称的属性时执行。
value(必需) 操作的第二个参数
type 用于表达操作的值类型(支持 longdouble 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>

JMXAccessorEqualsCondition: 等于 MBean Ant 条件

属性列表

属性 描述 默认值
url 设置 JMX 连接 URL - service:jmx:rmi:///jndi/rmi://127.0.0.1:8050/jmxrmi
host 设置主机,简化非常长的 URL 语法。 localhost
port 设置远程连接端口 8050
username 远程 JMX 连接用户名。
password 远程 JMX 连接密码。
ref 内部连接引用的名称。使用此属性,您可以在同一个 Ant 项目中配置多个连接。 jmx.server
name 完全限定的 JMX ObjectName -- Catalina:type=Server
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>

使用 JMXProxyServlet

Tomcat 提供了一种替代使用远程(甚至本地)JMX 连接的方法,同时仍然可以让您访问 JMX 提供的所有功能:Tomcat 的 JMXProxyServlet

JMXProxyServlet 允许客户端通过 HTTP 接口发出 JMX 查询。与直接从客户端程序使用 JMX 相比,此技术具有以下优点

  • 您不必启动完整的 JVM 并建立远程 JMX 连接,只需从运行的服务器请求一小部分数据
  • 您不必了解如何使用 JMX 连接
  • 您不需要本页其余部分中介绍的任何复杂配置
  • 您的客户端程序不必用 Java 编写

JMX 过度使用的完美示例可以在流行的服务器监控软件(如 Nagios 或 Icinga)中看到:如果您想通过 JMX 监控 10 个项目,您将不得不启动 10 个 JVM,建立 10 个 JMX 连接,然后每隔几分钟关闭它们。使用 JMXProxyServlet,您可以建立 10 个 HTTP 连接,然后完成操作。

您可以在 Tomcat 管理器 文档中找到有关 JMXProxyServlet 的更多信息。