Tomcat Manager

概述

很多生产环境都非常需要以下特性:在无需关闭或重启整个容器的情况下,部署新的 Web 应用或者取消对现有应用的部署。或者,即便在 Tomcat 服务器配置文件中没有指定 reloadable 的情况下,也可以请求重新加载现有应用。

Tomcat 中的 Web 应用 Manager 就是来解决这些问题的,它默认安装在上下文路径:/manager 中,支持以下功能:

  • 用已上传的 WAR 文件内容部署新的 Web 应用。
  • 在服务器文件系统中指定上下文路径处部署新的 Web 应用。
  • 列出当前已部署的 Web 应用,以及这些应用目前的活跃会话。
  • 重新加载现有的 Web 应用,以便响应 /WEB-INF/classes/WEB-INF/lib 中内容的更改。
  • 列出操作系统及 JVM 的属性值。
  • 列出可用的全局 JNDI 资源,它们将用于预备 <ResourceLink> 元素的部署工具中。<ResourceLink> 元素内嵌于 <Context> 部署描述中。
  • 开启一个已停止的 Web 应用,从而使其再次可用。
  • 停止一个现有的 Web 应用,从而使其不可用,但并不取消对它的部署。
  • 取消对一个已部署 Web 应用的部署,删除它的文档库目录(除非它是从文件系统中部署的)。

Tomcat 默认安装已经包含了 Manager。 将一个 Manager 应用实例的 Context 添加到一个新的主机中,manager.xml 上下文配置文件应放在 $CATALINA_BASE/conf/[enginename]/[hostname] 文件夹中。如下所示:

<Context privileged="true" antiResourceLocking="false"
         docBase="${catalina.home}/webapps/manager">
  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127.0.0.1" />
</Context>

如果将 Tomcat 配置成能够支持多个虚拟主机(网站),则需要对每个虚拟主机配置一个 Manager。

Manager 应用的使用方式有以下三种:

  • 作为带有用户界面的应用,在浏览器中运行。在随后这个范例 URL 中,你可以将 localhost 替换为你的网站主机名称:http://localhost:8080/manager/html
  • 只使用 HTTP 请求的一个功能最少的版本。它适合系统管理员通过创建脚本来进行使用。将命令指定在请求的 URI 中,响应是简单格式的文本(易于解析与处理)。详情查看 支持的 Manager 命令
  • 用于 Ant 构建工具(1.4或更新版本)的一套方便的任务定义。详情参见利用 Ant 执行 Manager 命令

配置 Manager 应用访问

下文的描述将使用变量名 $CATALINA_BASE 来指代工作目录。如果你还没有为多个 Tomcat 实例设置 CATALINA_BASE 目录,那么 $CATALINA_BASE 就将被设置为 $CATALINA_HOME(Tomcat 的安装目录)的值。

Tomcat 以默认值运行是非常危险的,因为这能让互联网上的任何人都可以在你的服务器上执行 Manager 应用。因此,Manager 应用要求任何用户在使用前必须验证自己的身份,提供自己的用户名和密码,以及相应配置的 manager-** 角色(角色名称根据所需的功能而定)。另外, 默认用户文件$CATALINA_BASE/conf/tomcat-users.xml)中的用户名称都没有指定角色名称,所以默认是不能访问 Manager 应用的。

这些角色名称位于 Manager 应用的 web.xml 文件中。可用的角色包括:

  • manager-gui 能够访问 HTML 界面。
  • manager-status 只能访问"服务器状态"(Server Status)页面。
  • manager-script 能够访问文档中描述的适用于工具的纯文本界面,以及"服务器状态"页面。
  • manager-jmx 能够访问 JMX 代理界面以及"服务器状态"(Server Status)页面。

HTML 界面不会遭受 CSRF(Cross-Site Request Forgery,跨站请求伪造)攻击,但纯文本界面及 JMX 界面却有可能无法幸免。这意味着,如果用户能够访问纯文本界面及 JMX 界面,那么在利用 Web 浏览器去访问 Manager 应用时,必须要万分谨慎。要想保持对 CSRF 免疫,则必须:

  • 在使用 Web 浏览器访问 Manager 应用时,假如用户具有 manager-scriptmanager-jmx 角色(比如为了测试纯文本界面或 JMX 界面),那么 必须 关闭所有的浏览器窗口,终止会话。如果不关闭浏览器,访问了其他站点,就可能会遭受 CSRF 攻击。
  • 建议永远不要将 manager-scriptmanager-jmx 角色授予那些拥有 manager-gui 角色的用户。

    注意 JMX 代理界面是 Tomcat 中非常高效的底层、类似于根级别的管理界面。如果用户知道了该调用的命令,就能实现大量行为,所以一定不要轻易授予用户 manager-jmx 角色。

为了能够访问 Manager 应用,必须创建一个新的用户名/密码组合,并为之授予一种 manager-** 角色,或者把一种 manager-** 角色授予现有的用户名/密码组合。因为本文档的大部分内容都在描述纯文本界面的命令,所以为了将来讨论实例方便起见,把角色名称定为 manager-script 。而涉及到具体如何配置用户名及密码,则是跟你所使用的Realm 实现有关:

  • UserDatabaseRealm 加上 MemoryUserDatabaseMemoryRealm ------ UserDatabaseRealmMemoryUserDatabase 配置在默认的 $CATALINA_BASE/conf/server.xml 文件中。 MemoryUserDatabaseMemoryRealm 都会读取储存在 CATALINA_BASE/conf/tomcat-users.xml 里的 XML 格式文件,它可以用任何文本编辑器进行编辑。该文件会为每个用户定义一个 XML 格式的 <user> ,如下所示:

    <user username="craigmcc" password="secret" roles="standard,manager-script" />

    它定义了用户登录时所用的用户名和密码,以及他或她采用的角色名称。你可以把 manager-script 角色添加到由逗号分隔的 roles 属性中,从而将该角色赋予一个或多个用户,也可以利用指定角色来创建新的用户。

  • DataSourceRealmJDBCRealm ------用户和角色信息都存储在一个经由 JDBC 访问的数据库中。按照当前环境的标准流程,将 manager-script 角色赋予一个或多个用户,或者利用该角色创建一个或多个新用户。

  • JNDIRealm ------你的用户和角色信息被存储在经由 LDAP 访问的一个目录服务器中。按照当前环境的标准流程,为一个或更多的现有用户添加 manager-script 角色,和(/或)利用指定角色创建一个或更多的新用户。

在下一节,当你第一次尝试使用 Manager 的一个命令时,将会使用 基本 验证进行登录。用户名和密码的具体内容并不重要,只要它们能够证明,用户数据库中拥有 manager-script 角色的用户是有效用户,我们的目的就达到了。

除了密码限制访问之外,Manager 还可以配置 RemoteAddrValveRemoteHostValve 这两个参数,分别通过 远程 IP 地址 或远程主机名来进行限制访问。详情可查看 Valve 文档。下列范例是通过 IP 地址来限制访问本地主机:

<Context privileged="true">
         <Valve className="org.apache.catalina.valves.RemoteAddrValve"
                allow="127.0.0.1"/>
</Context>  

易用的 HTML 界面

Manager 应用易用的 HTML 界面位于:

http://{host}:{port}/manager/html

正像前面讲过的那样,需要被授予 manager-gui 角色才能访问它。关于这个界面,还有一个独立的文档,请访问以下页面:

HTML 界面可免受 CSRF(跨站请求伪造)攻击。对 HTML 页面的每次访问都会生成一个随机令牌,存储在会话中,包含在页面的所有链接中。如果你的下一个操作没有正确的令牌值,操作就会被拒绝。如果令牌过期,可以从主页或者 Manager 的 List Applications (列出的应用)页面重新开始。

Manager 支持的命令

Manager 应用能够处理的命令都是通过下面这样的请求 URL 来指定的:

http://{host}:{port}/manager/text/{command}?{parameters}

{host}{port} 分别代表运行 Tomcat 服务器所在的主机名和端口号。{command} 代表所要执行的 Manager 命令。{parameters} 代表该命令所专有的查询参数。在后面的实例中,可以为你的安装自定义适当的主机和端口。

这些命令通常是被 HTTP GET 请求执行的。/deploy 命令有一种能够被 HTTP PUT 请求所执行的形式。

常见参数

多数 Manager 命令都能够接受一个或多个查询参数,这些查询参数如下所示:

  • path 要处理的 Web 应用的上下文路径(包含前面的斜杠)。要想选择 ROOT Web 应用,指定 / 即可。 注意 :无法对 Manager 应用自身执行管理命令。
  • version 并行部署 所用的 Web 应用版本号。
  • war Web 应用归档(WAR)文件的 URL,或者含有 Web 应用的目录路径名,或者是上下文配置 .xml 文件。你可以按照以下任一格式使用 URL:
    • file:/absolute/path/to/a/directory 解压缩后的 Web 应用所在的目录的绝对路径。它将不做任何改动,直接附加到你所指定的上下文路径上。
    • file:/absolute/path/to/a/webapp.war Web 应用归档(WAR)文件的绝对路径。 /deploy 命令有效,也是该命令所唯一能接受的格式。
    • jar:file:/absolute/path/to/a/warfile.war!/ 本地 WAR 文件的 URL。另外,为了能够完整引用一个JAR 文件,你可以采用 JarURLConnection 类的任何有效语法。
    • file:/absolute/path/to/a/context.xml Web 应用上下文配置 .xml 文件的绝对路径,上下文配置文件包含着上下文配置元素。
    • directory 主机应用的基本目录中的 Web 应用的目录名称。
    • webapp.war 主机应用的基本目录中的 WAR 文件的名称。

每个命令都会以 text/plain 的形式(比如,没有 HTML 标记的纯 ASCII 码文本 )返回响应,从而便于开发者与程序阅读。响应的第一行以 OKFAIL 开头,表明请求的命令是否成功。如果失败,响应第一行随后部分就会带有遇到问题的描述。一些包含其他信息行的命令会在下文予以介绍。

国际化说明 Manager 应用会在资源包中查找消息字符串,所以这些字符串可能已经转化为你所用平台的语言版本了。下文的范例展示的全都是消息的英文版本。

远程部署新应用

http://localhost:8080/manager/text/deploy?path=/foo

将作为请求数据指定在 HTTP PUT 请求中的 Web 应用归档文件(WAR)上传,将它安装到相应虚拟主机的 appBase 目录中,启动,使用目录名或不带 .war 后缀的 WAR 文件名作为路径。稍后,可以通过 /undeploy 取消对应用的部署,相应的应用目录也会被删除。

该命令通过 HTTP PUT 请求来执行。

通过在 /META-INF/context.xml 中包含上下文配置 XML 文件,.WAR 文件能够包含 Tomcat 特有的部署配置信息。

URL 参数包括:

  • update 设置为 true 时,任何已有的更新将会首先取消部署。默认值为 false。
  • tag 指定一个标签名称。这个参数能将已部署的 Web 应用与标签连接起来。如果 Web 应用被取消部署,则以后在需要重新部署时,只需使用标签就能实现。

注意 :该命令是 /undeploy 命令在逻辑上是对立的。

如果安装或启动成功,会接受到这样一个响应:

OK - Deployed application at context path /foo

否则,响应会以 FAIL 开始,并包含一个错误消息。出现问题的可能原因为:

  • Application already exists at path /foo
    当前运行的 Web 应用的上下文路径必须是唯一的。否则,必须使用这一上下文路径取消对现有 Web 应用的部署,或者为新应用选择另外一个上下文路径。update 参数可以指定为 URL 中的参数。true 值可避免这种错误。这种情况下,会在部署前,取消对现有应用的部署。

  • Encountered exception
    遇到试图开启新的 Web 应用。可查看 Tomcat 日志了解详情。但有可能是在解析 /WEB-INF/web.xml 文件时遇到了问题,或者在初始化应用的事件侦听器与过滤器时出现遗失类的情况。

从本地路径处部署新的应用

部署并启动一个新的 Web 应用,附加到指定的上下文 path 上(不能被其他 Web 应用同时使用)。该命令与 /undeploy 在逻辑上是对立的。

该命令由一个 HTTP GET 请求执行。部署命令的应用方式有很多种。

部署之前部署过的 Web 应用

http://localhost:8080/manager/text/deploy?path=/footoo&tag=footag

用来部署之前曾通过 tag 属性部署过的 Web 应用。注意,Manager 应用的工作目录包含之前部署过的 WAR 文件;如果清除它则将使部署失败。

通过 URL 部署一个目录或 WAR 文件

部署位于 Tomcat 服务器上的 Web 应用目录或 .war 文件。如果没有指定上下文路径参数 path,就会把目录名或未带 .war 后缀的 war 文件名当做路径来使用。war 参数指定了目录或 WAR 文件的 URL(也包含 file:格式)。引用 WAR 文件的 URL 所采用的语法详见 java.net.JarURLConnection 类的 Java 文档页面。只使用引用了整个 WAR 文件的 URL。

下面这个实例中,Web 应用位于 Tomcat 服务器上的 /path/to/foo 目录中,被部署为上下文路径为 /footoo 的 Web 应用。

http://localhost:8080/manager/text/deploy?path=/footoo&war=file:/path/to/foo

在下例中,Tomcat 服务器上的 .war 文件 /path/to/bar.war 被部署为上下文路径为 /bar 的 Web 应用。注意,这里没有 path 参数,因此上下文路径默认为没有 .war 后缀的 WAR 文件名。

http://localhost:8080/manager/text/deploy?war=jar:file:/path/to/bar.war!/

从主机的 appBase 目录中部署一个目录或 WAR

对位于主机 appBase 目录中的 Web 应用目录或 .war 文件进行部署。目录名或没有 .war 后缀名的 WAR 文件名被用作上下文路径名。

在下面的范例中,Web 应用位于 Tomcat 服务器中主机 appBase 目录下名为 foo 的子目录中,被部署为上下文路径名为 /foo 的 Web 应用。注意,用到的上下文路径名就是 Web 应用的目录名。

http://localhost:8080/manager/text/deploy?war=foo

在下面的范例中,位于主机 appBase 目录中的 bar.war 文件被部署为上下文名为 /bar 的 Web 应用。

http://localhost:8080/manager/text/deploy?war=bar.war

使用上下文配置 .xml 文件来进行部署

如果主机的 deployXML 标志设定为 true,就可以使用上下文配置 .xml 文件以及一个可选的 .war 文件(或 Web 应用目录)来进行 Web 应用部署。在使用上下文 .xml 文件配置文件进行部署时,不会用到上下文路径参数 /path

上下文配置 .xml 文件包含用于 Web 应用上下文的有效 XML,就好像是在 Tomcat 的 server.xml 配置文件中进行配置一样。范例如下:

<Context path="/foobar" docBase="/path/to/application/foobar">
</Context>

可选的 war 参数被设定为指向 Web 应用的 .war 文件或目录的 URL,它会覆盖掉上下文配置 .xml 文件中的任意 docBase

在下面这个实例中,使用上下文配置 .xml 文件部署 Web 应用:

http://localhost:8080/manager/text/deploy?config=file:/path/context.xml

在下面这个应用部署范例中,使用了上下文配置 .xml 文件和位于服务器中的 Web 应用的 .war 文件。

http://localhost:8080/manager/text/deploy
 ?config=file:/path/context.xml&war=jar:file:/path/bar.war!/

部署中的一些注意事项

如果主机配置中将 unpackWARs 设为 true,而且你部署了一个 war 文件,那么这个 war 文件将解压缩至主机的 appBase 目录下的一个目录中。

如果应用的 war 文件或目录安装在主机的 appBase 目录中,那么或者主机应该被部署为 autoDeploy 为 true,或者上下文路径必须匹配目录名或不带 .war 后缀的 war 文件名。

为了避免不可信用户作出对 Web 应用的侵害,主机的 deployXML 标志可以设为 false。这能保证不可信用户通过使用 XML 配置文件来部署 Web 应用,也能阻止他们部署位于其主机 appBase 之外的应用目录或 .war 文件。

部署响应

如果安装及启动都正常,会得到以下这样的响应:

OK - Deployed application at context path /foo

否则,响应会以 FAIL 开头并包含一些错误消息,引起问题的原因可能有以下几种:

  • Application already exists at path /foo
    当前运行的 Web 应用的上下文路径必须是唯一的。否则,必须使用这一上下文路径取消对现有 Web 应用的部署,或者为新应用选择另外一个上下文路径。update 参数可以指定为 URL 中的参数。true 值可避免这种错误。这种情况下,会在部署前,取消对现有应用的部署。

  • Document base does not exist or is not a readable directory
    通过 war 指定的 URL 必须要确认服务器中的某个目录含有解压缩后的 Web 应用,包含该应用的 WAR 文件的绝对 URL 。更正 war 参数所提供的值。

  • Encountered exception
    遇到试图开启新 Web 应用。可查看 Tomcat 日志了解详情。但有可能是在解析 /WEB-INF/web.xml 文件时遇到了问题,或者在初始化应用的事件侦听器与过滤器时出现遗失类的情况。

  • Invalid application URL was specified 所指定的指向目录或 Web 应用的 URL 无效。有效的 URL 必须以 file: 开始,用于 WAR 文件的 URL 必须以 .war 结尾。

  • Invalid context path was specified
    上下文路径必须以斜杠字符开始,引用 ROOT 应用必须使用 /

  • Context path must match the directory or WAR file name
    如果应用的 .war 文件或目录安装在主机的 appBase 目录,那么或者主机应该被部署为 autoDeploy 为 true,或者上下文路径必须匹配目录名或不带 .war 后缀的 war 文件名。

  • Only web applications in the Host web application directory can be installed 如果主机的 deployXML 标志为设为 false,那么当要部署的 Web 应用目录或 .war 文件位于主机 appBase 目录之外时,就会产生这样的错误。

列出当前已部署的应用

http://localhost:8080/manager/text/list

列出当前所有部署的 Web 应用的上下文路径、当前状态(runningstopped),以及活跃会话。开启 Tomcat 后,一般立刻会产生如下这样的响应:

OK - Listed applications for virtual host localhost
/webdav:running:0
/examples:running:0
/manager:running:0
/:running:0

^Listed applications for virtual host localhost:列出虚拟主机本地主机的所有应用。^

重新加载一个现有应用

http://localhost:8080/manager/text/reload?path=/examples

标记一个现有应用,关闭它并重新加载。这一功能的适用情况为:当 Web 应用上下文不能重新加载;你已经更新了 /WEB-INF/classes 目录中的类和属性文件时;或者当你在 /WEB-INF/lib 目录添加或更新了 jar 文件。

注意 :在重新加载时,Web 应用配置文件 /WEB-INF/web.xml无法重新读取。如果对 web.xml 文件作出改动,则必须停止并启动 Web 应用。

如果命令成功执行,应得如下所示的响应:

OK - Reloaded application at context path /examples

否则,返回的响应以 FAIL 开头,并包含相关的错误消息。引起问题的可能原因有以下几种:

  • Encountered exception
    遇到试图重启 Web 应用的异常。可查看 Tomcat 日志了解详情。

  • Invalid context path was specified
    上下文路径必须以斜杠开始,引用 ROOT Web 应用必须使用 /

  • No context exists for path /foo
    在所指定的上下文路径中没有发现部署好的应用。

  • No context path was specified
    需要 path 参数。

  • Reload not supported on WAR deployed at path /foo
    当前,如果主机配置为不解压缩 WAR 文件时,直接从一个 WAR 文件安装 Web 应用时,不支持重新加载应用(以便使类或 web.xml 文件中的更改生效)。由于只有在从已解压缩目录安装 Web 应用时才生效,所以在使用 WAR 文件时,应该先取消对应用的部署,然后重新部署该应用,以便使更改生效。

列出 OS 及 JVM 属性

http://localhost:8080/manager/text/serverinfo

列出 Tomcat 版本、操作系统以及 JVM 属性等相关信息。

如果出现错误,响应会以 FAIL 开始并包含一系列错误消息,导致错误的可能原因包括有:

  • Encountered exception
    碰到异常试图列举系统属性。可查看 Tomcat 日志了解详情。

列出可能的全局 JNDI 资源

http://localhost:8080/manager/text/resources[?type=xxxxx]

列出上下文配置文件资源链接中所使用的全局 JNDI 资源。如果指定 type 请求参数,参数值必须是所需资源类型的完整 Java 类名(比如,指定 javax.sql.DataSource 获取所有可用的 JDBC 数据资源的名称)。如果没有指定 type 请求参数,则将返回所有类型的资源。

根据是否指定了 type 请求参数,常见响应的第一行将如下所示:

OK - Listed global resources of all types

OK - Listed global resources of type xxxxx

后面将每个资源都单列一行,每一行内的字段都由冒号(:)分隔,如下所示:

  • Global Resource Name 全局 JNDI 资源的名称,将用在 <ResourceLink> 元素的 global 属性中。
  • Global Resource Type 该全局 JNDI 资源的完整描述的 Java 类名。

如果出现错误,响应将会以 FAIL 开始,并包含一个错误消息。出错的原因可能包括以下几方面:

  • Encountered exception
    碰到异常试图列举 JNDI 资源,可查看 Tomcat 日志了解详情。

  • No global JNDI resources are available
    运行的 Tomcat 服务器没有配置全局 JNDI 资源。

会话统计

http://localhost:8080/manager/text/sessions?path=/examples

显示 Web 应用默认的会话超时,当前活跃会话在一分钟范围内实际的超时次数。比如,重启 Tomcat 并随后执行 /examples Web 应用中的一个 JSP 范例,有可能得到如下信息:

OK - Session information for application at context path /examples
Default maximum session inactive interval 30 minutes
<1 minutes: 1 sessions
1 - <2 minutes: 1 sessions

过期会话

http://localhost:8080/manager/text/expire?path=/examples&idle=num

显示会话统计信息(比如上面的 /sessions 命令)以及超出 num 所指定的分钟数的过期会话。要想使所有会话都过期,可使用 &idle = 0

OK - Session information for application at context path /examples
Default maximum session inactive interval 30 minutes
1 - <2 minutes: 1 sessions
3 - <4 minutes: 1 sessions
>0 minutes: 2 sessions were expired

实际上,/sessions/expire 是同一个命令的两种异名,唯一不同之处在于 idle 参数。

开启一个现有应用

http://localhost:8080/manager/text/start?path=/examples

标记一个已停止的应用,重新开启它,使其再次可用。停止并随后重新开启应用有时显得非常重要,比如当应用所需的服务器暂时变得不可用时。通常情况下,与其让用户频繁碰到数据库异常,倒不如停止基于该数据库的 Web 应用运行。

如果该命令成功执行,将得到类似如下的响应:

OK - Started application at context path /examples

否则,将返回出错响应,该响应以 FAIL 开头,并包含一些错误。出错原因可能是由于:

  • Encountered exception
    碰到异常情况,试图开启 Web 应用。可检查 Tomcat 日志了解详情。

  • Invalid context path was specified
    上下文路径必须以斜杠字符开始,引用 ROOT Web 应用必须使用反斜杠(/)。

  • No context exists for path /foo
    在指定的上下文路径处没有部署的应用。

  • No context path was specified
    需要指定 path 参数。

停止已有应用

http://localhost:8080/manager/text/stop?path=/examples

标记现有应用,使其不可用,但仍使其处于已部署状态。当应用停止时,任何请求都将得到著名的 HTTP 404错误。在应用列表中,该应用将显示为"stopped"。

如果该命令成功执行,将得到类似如下的响应:

OK - Stopped application at context path /examples

否则,将返回出错响应,它以 FAIL 开头,并包含一个出错消息,可能导致出误的原因包括:

  • Encountered exception
    碰到异常情况,试图开启 Web 应用。可检查 Tomcat 日志了解详情。

  • Invalid context path was specified
    上下文路径必须以斜杠字符开始,引用 ROOT Web 应用必须使用反斜杠(/)。

  • No context exists for path /foo 在指定的上下文路径处没有部署的应用。

  • No context path was specified
    需要指定 path 参数。

取消对现有应用的部署

http://localhost:8080/manager/text/undeploy?path=/examples

警告: 该命令将删除虚拟主机 appBase 目录(通常是 webapps )中的所有 Web 应用。 该命令将从未解压缩(或已解压缩)的 .WAR 式部署中,以及 $CATALINA_BASE/conf/[enginename]/[hostname]/ 中以 XML 格式保存的上下文描述符中,删除应用的 .WAR 文件及目录。如果你只是想让某个应用暂停服务,则应该使用 /stop 命令。

标记一个已有的应用,将其恰当地关闭,从 Tomcat 中移除(从而使得以后可以重新使用该上下文路径)。另外,如果文档根目录位于虚拟主机的 appBase 目录(通常是 webapps)中,则它也将被移除。该命令是 /deploy 的逆向命令。

如果该命令成功执行,将得到类似如下的响应:

OK - Undeployed application at context path /examples

否则,将返回出错响应,它以 FAIL 开头,并包含一个出错消息,可能导致出误的原因包括:

  • Encountered exception
    碰到异常情况,试图取消对某个 Web 应用的部署。可检查 Tomcat 日志了解详情。

  • Invalid context path was specified
    上下文路径必须以斜杠字符开始,引用 ROOT Web 应用必须使用反斜杠(/)。

  • No context exists for path /foo 在指定的上下文路径处没有部署的应用。

  • No context path was specified
    需要指定 path 参数。

寻找内存泄露

http://localhost:8080/manager/text/findleaks[?statusLine=[true|false]]

寻找内存泄露的诊断将触发一个彻底的垃圾回收(GC)方案,所以如果在生产环境中使用它,需要非常谨慎才行。

寻找内存泄露的诊断会试图确认已导致内存泄露的 Web 应用(当其处于停止、重新加载,以及被取消部署状态时)。通常由一种分析器来确认结论。诊断使用了由 StandardHost(标准主机)实现所提供的附加功能。如果使用的是没有扩展自 StandHost 的自定义主机,则该诊断无法生效。

已有一些文档介绍,从 Java 代码中显式地触发彻底的垃圾回收方案是不可靠的。此外,在不同的 JVM 中,也有很多选项禁止显式触发垃圾回收,比如像 -XX:+DisableExplicitGC。 如果你需要确认诊断是否成功地实现了彻底的垃圾回收,可以使用 GC 日志、JConsole 分析器,或其他类似工具。

如果该命令成功执行,将得到类似如下的响应:

/leaking-webapp

如果你希望在响应中看到状态行,那么可以在请求中加入 statusLine 查询参数,并将其设定为 true

对于已停止运行、被重新加载或被取消部署的Web 应用,由于之前运行所用到的类可能仍然加载在内存中,从而会造成内存泄露。响应将把这种应用的每个上下文路径都单列一行。如果应用被重新加载了数次,就可能会列出几次。

如果命令并没有成功执行,响应将以 FAIL 开头,并包含一个错误消息。

连接器 SSL/TLS 诊断

http://localhost:8080/manager/text/sslConnectorCiphers

SSL 连接器/加密诊断会列出当前每一连接器所配置的 SSL/TLS 加密算法。对于 BIO 和 NIO,将列出每个加密算法套件的名称;对于 APR,则返回 SSLCipherSuite 的值。

响应类似如下所示:

OK - Connector / SSL Cipher information
Connector[HTTP/1.1-8080]
  SSL is not enabled for this connector
Connector[HTTP/1.1-8443]
  TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
  TLS_DHE_RSA_WITH_AES_128_CBC_SHA
  TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
  TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
  ...

线程转储

http://localhost:8080/manager/text/threaddump

编写 JVM 线程转储。

响应类似如下所示:

OK - JVM thread dump
2014-12-08 07:24:40.080
Full thread dump Java HotSpot(TM) Client VM (25.25-b02 mixed mode):

"http-nio-8080-exec-2" Id=26 cpu=46800300 ns usr=46800300 ns blocked 0 for -1 ms waited 0 for -1 ms
   java.lang.Thread.State: RUNNABLE
        locks java.util.concurrent.ThreadPoolExecutor$Worker@1738ad4
        at sun.management.ThreadImpl.dumpThreads0(Native Method)
        at sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:446)
        at org.apache.tomcat.util.Diagnostics.getThreadDump(Diagnostics.java:440)
        at org.apache.tomcat.util.Diagnostics.getThreadDump(Diagnostics.java:409)
        at org.apache.catalina.manager.ManagerServlet.threadDump(ManagerServlet.java:557)
        at org.apache.catalina.manager.ManagerServlet.doGet(ManagerServlet.java:371)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
...

虚拟机(VM)相关信息

http://localhost:8080/manager/text/vminfo

写入一些关于 Java 虚拟机(JVM)的诊断信息。

响应类似如下所示:

OK - VM info
2014-12-08 07:27:32.578
Runtime information:
  vmName: Java HotSpot(TM) Client VM
  vmVersion: 25.25-b02
  vmVendor: Oracle Corporation
  specName: Java Virtual Machine Specification
  specVersion: 1.8
  specVendor: Oracle Corporation
  managementSpecVersion: 1.2
  name: ...
  startTime: 1418012458849
  uptime: 393855
  isBootClassPathSupported: true

OS information:
...

保存配置信息

http://localhost:8080/manager/text/save

如果不指定任何参数,该命令将把服务器的当前配置信息保存到 server.xml 中。已有的配置信息 .xml 文件将被重命名,作为必要时的备份文件。

如果指定了 path 参数,而且该参数与已部署应用的路径相匹配,那么该 Web 应用的配置将保存为一个命名恰当的上下文 .xml 文件中,位于当前主机的 xmlBase 中。

要想使用该命令,则 StoreConfig MBean 必须存在。通常需要用 StoreConfigLifecycleListener来配置。

如果命令不能成功执行,响应将以 FAIL 开头,并包含一个错误消息。

服务器状态

可从下面这些链接中观察有关服务器的状态信息。任何一个 manager-** 角色都能访问这一页面。

http://localhost:8080/manager/status
http://localhost:8080/manager/status/all

上面是用 HTML 格式显示服务器状态信息的命令。

http://localhost:8080/manager/status?XML=true
http://localhost:8080/manager/status/all?XML=true

上面是用 XML 格式显示服务器状态信息的命令。

首先,显示的是服务器和 JVM 的版本号、JVM 提供者、操作系统的名称及其版本号,然后还显示了系统体系架构类型。

其次,显示的是关于 JVM 的内存使用信息。

最后,显示的是关于 Tomcat AJP 和 HTTP 连接器的信息。对两者来说,这些信息都很有用:

  • 线程信息:最大线程数、最少及最多的空闲线程数、当前线程数量以及当前繁忙线程。
  • 请求信息:最长及最短的处理时间、请求和错误的数量,以及接受和发送的字节数量。
  • 一张完整显示线程阶段、时间、发送字节数、接受字节数、客户端、虚拟主机及请求的表。它将列出所有现有线程。下面列出了所有可能的线程阶段:

    • 解析及准备请求 将对请求报头进行解析,或进行必要的准备,以便读取请求主体(如果指定了传输编码)。
    • 服务 线程处理请求并生成响应。该阶段中至少有一个线程(可查看服务器状态页)。
    • 完成 请求处理结束。所有仍在输出缓冲区中的剩余响应都被传送至客户端。如果有必要保持连接活跃,则下一个阶段是"持续活跃"阶段,否则接下来直接进入"就绪"阶段。
    • 持续活跃 当客户端发送另一请求时,线程能使连接对客户端保持开放。如果接收到另一请求,下一阶段就将是"解析及准备请求"阶段。如果持续活跃超时结束,仍没有接收到请求,则连接关闭,进入下一阶段"就绪"阶段。
    • 就绪 线程空闲,等待再此被使用。

使用 /status/all 命令可查看每一个已配置 Web 应用的额外信息。

使用 JMX 代理 Servlet

什么是 JMX 代理 Servlet

JMX 代理 Servlet 是一款轻量级的代理。它的用途对用户来说并不是特别友好,但是其 UI 却非常有助于整合命令行脚本,从便于监控和改变 Tomcat 的内部运行。通过这个代理,我们可以获取和设置信息。要想真正了解 JMX 代理 Servlet,首先应该大概了解 JMX。如果不知道 JMX 的基本原理,那有些内容就很难理解了。

JMX 查询命令

JMX 的查询命令格式如下所示:

http://webserver/manager/jmxproxy/?qry=STUFF

STUFF 是所要执行的 JMX 查询。比如,可以执行以下这些查询:

  • qry=*%3Atype%3DRequestProcessor%2C* --> type=RequestProcessor 定位所有能够处理请求并汇报各自状态的 Worker。
  • qry=*%3Aj2eeType=Servlet%2c* --> j2eeType=Servlet 查询返回所有加载的 Servlet。
  • qry=Catalina%3Atype%3DEnvironment%2Cresourcetype%3DGlobal%2Cname%3DsimpleValue --> Catalina:type=Environment,resourcetype=Global,name=simpleValue 按照指定名称查找 MBean。

需要实际地试验一下才能真正理解这些功能。如果没有提供 qry 参数,则将显示全部的 MBean。我们强烈建议你去阅读 Tomcat 源代码,真正了解 JMX 规范,更好地掌握所有能够执行的查询。

JMX 的 get 命令

JMXProxyServlet 还支持一种 get 命令来获取特定 MBean的属性值。该命令的一般格式如下所示:

http://webserver/manager/jmxproxy/?get=BEANNAME&att=MYATTRIBUTE&key=MYKEY

必须提供如下参数:

  1. get:MBean 的完整名称。
  2. att:希望获取的属性。
  3. key:(可选参数)CompositeData MBean 的属性中的键。

如果命令成功执行,则一切正常,否则就会返回一个出错消息。举两个例子,比如当希望获取当前的堆内存数据时,可以采用如下命令:

http://webserver/manager/jmxproxy/?get=java.lang:type=Memory&att=HeapMemoryUsage

再或者,如果只希望获取"用过的"键,可以采用如下命令:

http://webserver/manager/jmxproxy/?get=java.lang:type=Memory&att=HeapMemoryUsage&key=used

JMX 的 set 命令

上面介绍了如何查询一个 MBean。下面来看看 Tomcat 的内部运行吧!set 命令的一般格式为:

http://webserver/manager/jmxproxy/?set=BEANNAME&att=MYATTRIBUTE&val=NEWVALUE

需要提供三个请求参数:

  • set:完整的 bean 名称。
  • att:想要改变的属性。
  • val:新的属性值。

如果命令成功执行,则一切正常,否则就会返回一个出错消息。比如,假如想为 ErrorReportValve 进行立即调试,可以将属性 debug 设为 10:

http://localhost:8080/manager/jmxproxy/
 ?set=Catalina%3Atype%3DValve%2Cname%3DErrorReportValve%2Chost%3Dlocalhost
 &att=debug&val=10  

所得结果如下(你的有可能不同):

Result: ok

下面来看看如果传入一个不恰当数值时的情况,比如使用一个URL,并试图将属性 debug 设置为 'cow'。

http://localhost:8080/manager/jmxproxy/
 ?set=Catalina%3Atype%3DValve%2Cname%3DErrorReportValve%2Chost%3Dlocalhost
 &att=debug&val=cow

运行结果如下:

Error: java.lang.NumberFormatException: For input string: "cow"

JMX 的 invoke 命令

使用 invoke 命令,我们就可以在 MBean 中调用方法。该命令的一般格式为:

http://webserver/manager/jmxproxy/
 ?invoke=BEANNAME&op=METHODNAME&ps=COMMASEPARATEDPARAMETERS

比如,使用如下方式来调用 ServicefindConnectors() 方法:

http://localhost:8080/manager/jmxproxy/
 ?invoke=Catalina%3Atype%3DService&op=findConnectors&ps=

利用 Ant 执行 Manager 的命令

上面的文档介绍了如何利用 HTTP 请求来执行 Manager 的命令。除此之外,Tomcat 还专为 Ant(1.4 版或更新版本)构建工具准备了一套方便的任务定义。为了使用这些命令,必须执行下面这些操作:

  • 下载 Ant 二进制分发包,地址为:http://ant.apache.org。必须使用 1.4 版本或更新版本。
  • 将分发包安装到合适的目录中(下面将把它叫做 ANT_HOME)。
  • 将文件 server/lib/catalina-ant.jar 从 Tomcat 安装目录中复制到 Ant 的库目录($ANT_HOME/lib)。
  • $ANT_HOME/bin 目录添加到环境变量 PATH 中。
  • 在 Tomcat 用户数据库中,至少配置一个拥有 manager-script 角色的用户名/密码组合数据。

为了在 Ant 中使用自定义任务,必须首先用 <taskdef> 元素来声明它们,因而 build.xml 文件应类似如下这样:

<project name="My Application" default="compile" basedir=".">

  <!-- Configure the directory into which the web application is built -->
  <property name="build"    value="${basedir}/build"/>

  <!-- Configure the context path for this application -->
  <property name="path"     value="/myapp"/>

  <!-- Configure properties to access the Manager application -->
  <property name="url"      value="http://localhost:8080/manager/text"/>
  <property name="username" value="myusername"/>
  <property name="password" value="mypassword"/>

  <!-- Configure the custom Ant tasks for the Manager application -->
  <taskdef name="list"      classname="org.apache.catalina.ant.ListTask"/>
  <taskdef name="deploy"    classname="org.apache.catalina.ant.DeployTask"/>
  <taskdef name="start"     classname="org.apache.catalina.ant.StartTask"/>
  <taskdef name="reload"    classname="org.apache.catalina.ant.ReloadTask"/>
  <taskdef name="stop"      classname="org.apache.catalina.ant.StopTask"/>
  <taskdef name="undeploy"  classname="org.apache.catalina.ant.UndeployTask"/>
  <taskdef name="resources" classname="org.apache.catalina.ant.ResourcesTask"/>
  <typedef name="sessions"  classname="org.apache.catalina.ant.SessionsTask"/>
  <taskdef name="findleaks" classname="org.apache.catalina.ant.FindLeaksTask"/>
  <typedef name="vminfo"    classname="org.apache.catalina.ant.VminfoTask"/>
  <typedef name="threaddump" classname="org.apache.catalina.ant.ThreaddumpTask"/>
  <typedef name="sslConnectorCiphers" classname="org.apache.catalina.ant.SslConnectorCiphersTask"/>

  <!-- Executable Targets -->
  <target name="compile" description="Compile web application">
    <!-- ... construct web application in ${build} subdirectory, and
            generated a ${path}.war ... -->
  </target>

  <target name="deploy" description="Install web application"
          depends="compile">
    <deploy url="${url}" username="${username}" password="${password}"
            path="${path}" war="file:${build}${path}.war"/>
  </target>

  <target name="reload" description="Reload web application"
          depends="compile">
    <reload  url="${url}" username="${username}" password="${password}"
            path="${path}"/>
  </target>

  <target name="undeploy" description="Remove web application">
    <undeploy url="${url}" username="${username}" password="${password}"
            path="${path}"/>
  </target>

</project>

注意:上面的资源任务定义将覆盖 Ant 1.7 中所添加的资源数据类型。如果你希望使用这些资源数据类型,需要使用 Ant 命名空间支持,将 Tomcat 的任务分配到它们自己的命名空间中。

现在,可以执行类似 ant deploy 这样的命令将应用部署到 Tomcat 的一个运行实例上,或者利用 ant reload 通知 Tomcat 重新加载应用。另外还需注意的是,在这个 build.xml 文件中,多数比较有价值的属性值都是可以被可替换的,因而可以利用命令行方式来重写这些值。比如,考虑到在 build.xml 文件中包含真正的管理员密码是非常危险的,可以通过一些命令来忽略密码属性,如下所示:

ant -Dpassword=secret deploy

任务输出捕获

使用 Ant 1.6.2 版或更新版本,Catalina 任务提供选项,利用属性或外部文件捕获输出。它们直接支持 <redirector> 类型属性的子集:

属性 属性说明 是否必需
output 输出文件名。如果错误流没有重定向到一个文件或属性上,它将出现在输出中。
error 命令的标准错误应该被重定向到的文件。
logError 用于在 Ant 日志中显示错误输出,将输出重定向至某个文件或属性。错误输出不会包含在输出文件或属性中。如果利用 errorerrorProperty 属性重定向错误,则没有任何效果。
append 输出和错误文件是否应该附加或覆盖。默认为 false
createemptyfiles 是否应该创建输出和错误文件,哪怕是空的文件。默认为 true
outputproperty 用于保存命令输出的属性名。除非错误流被重定向至单独的文件或流,否则这一属性将包含错误输出。
errorproperty 用于保存命令标准错误的属性名。

还可以指定其他一些额外属性:

属性 属性说明 是否必需
alwaysLog 该属性用于查看捕获的输出,这个输出也出现在 Ant 日志中。除非捕获任务输出,否则千万不要使用它。默认为 falseAnt 1.6.3 通过 <redirector> 直接支持该属性。
failonerror 用于避免因为 manager 命令处理中错误而导致 Ant 执行终止情况的发生。默认为 true。如果希望捕获错误输出,则必须设为false,否则 Ant 执行将有可能在未捕获任何输出前就被终止。该属性只用于 manager 命令的执行上,任何错误的或丢失的命令属性仍然会导致 Ant 执行终止。

它们还支持内嵌的 <redirector> 元素,你可以在这些元素中指定全套的属性。但对于inputinputstringinputencoding,即使接收,也无法使用,因为在这种上下文中它们没有任何意义。详情可参考 Ant 手册以了解 <redirector> 元素的各个属性。

下面这个范例摘录了一段构建文件,展示了这种对输出重定向的支持是如何运作的。

    <target name="manager.deploy"
        depends="context.status"
        if="context.notInstalled">
        <deploy url="${mgr.url}"
            username="${mgr.username}"
            password="${mgr.password}"
            path="${mgr.context.path}"
            config="${mgr.context.descriptor}"/>
    </target>

    <target name="manager.deploy.war"
        depends="context.status"
        if="context.deployable">
        <deploy url="${mgr.url}"
            username="${mgr.username}"
            password="${mgr.password}"
            update="${mgr.update}"
            path="${mgr.context.path}"
            war="${mgr.war.file}"/>
    </target>

    <target name="context.status">
        <property name="running" value="${mgr.context.path}:running"/>
        <property name="stopped" value="${mgr.context.path}:stopped"/>

        <list url="${mgr.url}"
            outputproperty="ctx.status"
            username="${mgr.username}"
            password="${mgr.password}">
        </list>

        <condition property="context.running">
            <contains string="${ctx.status}" substring="${running}"/>
        </condition>
        <condition property="context.stopped">
            <contains string="${ctx.status}" substring="${stopped}"/>
        </condition>
        <condition property="context.notInstalled">
            <and>
                <isfalse value="${context.running}"/>
                <isfalse value="${context.stopped}"/>
            </and>
        </condition>
        <condition property="context.deployable">
            <or>
                <istrue value="${context.notInstalled}"/>
                <and>
                    <istrue value="${context.running}"/>
                    <istrue value="${mgr.update}"/>
                </and>
                <and>
                    <istrue value="${context.stopped}"/>
                    <istrue value="${mgr.update}"/>
                </and>
            </or>
        </condition>
        <condition property="context.undeployable">
            <or>
                <istrue value="${context.running}"/>
                <istrue value="${context.stopped}"/>
            </or>
        </condition>
    </target>

警告 :多次调用 Catalina 任务往往并不是一个好主意,退一步说这样做的意义也不是很大。如果 Ant 任务依赖链设定糟糕的话,即使本意并非如此,也会导致在一次 Ant 运行中多次运行任务。必须提前对你稍加警告,因为有可能当你从任务中捕获输出时,会出现一些意想不到的情况:

  • 当用属性捕获时,你将只能从其中找到 最初 调用的输出,因为 Ant 属性是不变的,一旦设定就无法改变。
  • 当用文件捕获时,你将只能从其中找到 最后 调用的输出,除非使用 append = "true" 属性------在这种情况下,你将看到附加在文件内容末尾的每一个任务调用的相关输出。