监控和管理 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

为了简化 JMX 在 Ant 中的使用,提供了一组可与 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://localhost: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://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 任务的属性在所有其他任务和条件中也存在。

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>

在虚拟主机 localhost 创建新访问日志 Valve 的示例

 <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 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.servletExamples.0.[manager attribute names]} 访问此管理器的所有属性。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 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 回显命令用法(访问和结果) 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 的 create
操作。

JMXAccessorUnregisterTask: 远程注销 MBean Ant 任务

属性列表

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

注销远程 MBean 的示例

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

警告:许多 Tomcat MBean 无法注销。
MBean 未与其父级解除链接。请改用 MBeanFactory
remove 操作。

JMXAccessorCondition: 表达条件

属性列表

属性 描述 默认值
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 回显条件用法(访问和结果) false
if 仅当当前项目中存在给定名称的属性时才执行。
unless 仅当当前项目中不存在给定名称的属性时才执行。
value (必需) 操作的第二个参数
type 表达操作的值类型(支持 longdouble long
operation 表达式
  • == 等于
  • != 不等于
  • > 大于 (&gt;)
  • >= 大于或等于 (&gt;=)
  • < 小于 (&lt;)
  • <= 小于或等于 (&lt;=)
==

等待服务器连接且集群备份节点可访问

<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://localhost: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 的信息。