Jade Dungeon

Tomcat权威指南

Tomcat开幕式

指定用户

useradd -g 46 -s /sbin/nologin -d /opt/tomcat/temp tomcat
  • -g:用户组。
  • -s:禁用shell并锁定用户名密码。
  • -d:home目录。

启动与停止

启动与停止:

bin/catalina.sh run     # 启动
bin/catalina.sh start   # 启动,但输出重定向到文件
bin/startup             # 同上一条

bin/catalina.sh stop    # 停止
bin/shutdown.sh         # 停止

startupcatalina.sh run会把输入重定向到文件: $TOMCAT_HOME/logs/catalina.out

catalina脚本启动参数

-config <server.xml file> 指定server.xml配置文件
-nonaming 禁用JNDI
debug 调试模式
embedded 在嵌入模式中测试,开发时常用
jpda start JPDA调试器方式启动

设置启动参数到全局Java环境变量:

export JAVA_OPTS="-Xmx256M"

其他重要参数:

CATALINA_HOME 静态程序部分
CATALINA_BASE 动态的网站与配置部分,包含:conf、logs、temp、webapps、work
CATALINA_OPTS 传递给Java的参数
CATALINA_TMPDIR 临时文件的目录

重启

要重启时判断什么时候真正被关掉是一个麻烦。shutdown.sh以后要查看进程是不是还在。

方法一:java带的命令:

jps | grep Bootstrap

方法二:系统工具;

ps auwwx | grep catalina.startup.Bootstrap

常见错误

端口占用

Linux:

netstat -a -tcp
  • -a:显示激活状态的连接。
  • -tcp:TCP连接。

BSD:

netstat -a -f inet
  • -f inet:只显示 Internet[IPV4]连接。

配置Tomcat

指定独立网站目录

生成一专门的tomcat用户与用户组:

sudo groupadd tomcat
sudo useradd tomcat -g tomcat -d /home/tomcat -s /bin/bash
sudo mkdir /home/tomcat
sudo chgrp -R tomcat /home/tomcat
sudo chown -R tomcat /home/tomcat

生成目录放所有的网站,下面各个网站再那家独立的目录:

cd /home/tomcat
mkdir tomcat-instance
cd tomcat-instance
mkdir groovywigs.com

网站目录下复制tomcat程序里的config目录,还有其他目录。

例,不带管理工具的版本:

cp -a $CATALINA_HOME/conf .
mkdir webapps logs temp work

例,带管理工具的版本:

cp -a $CATALINA_HOME/conf .
cp -a $CATALINA_HOME/webapps .
mkdir logs temp work

编辑conf/server.xml,设置端口与目录位置:

  • Shutdown port:此端口用于关闭Tomcat。当执行shutdown.sh脚本时,它会给此端口 发出一个信号,Tomcat的进程会监听此端口,如果接收到这样的信号,进程会清理退出 。
  • Connector port:此端口是应用对外公开发布的端口。
  • AJP port:Web服务器(例如Apache的httpd Server)通过此端口和Tomcat进行通信, 也可以使用它设置一个负载均衡服务器。
  • Redirect port:如果此Connector支持非SSL请求和接收SSL请求,Catalina会自动 将请求指向到此端口。
<!-- server port -->
<Server port="8105" shutdown="SHUTDOWN">

	<Service name="Catalina">
	
		<!-- connector port -->
		<Connector port="8180" protocol="HTTP/1.1" 
			connectionTimeout="20000" 
			redirectPort="8443" />                      <!-- redirect Port -->

		<!-- ajp port -->
		<Connector port="8009" protocol="AJP/1.3" 
			redirectPort="8443" />                     <!-- redirect Port -->

	</Service>

</Server>

建立启动脚本bin/startup.sh

#!/bin/sh
export CATALINA_HOME=/opt/morganstudio/server/tomcat8 
export CATALINA_BASE=/home/tomcat/tomcat-instance/groovywigs.com              
$CATALINA_HOME/bin/catalina.sh run

建立停止脚本bin/shutdown.sh

#!/bin/sh                                                                                 
export CATALINA_HOME=/opt/morganstudio/server/tomcat8 
export CATALINA_BASE=/home/tomcat/tomcat-instance/groovywigs.com              
$CATALINA_HOME/bin/catalina.sh stop 

Java VM配置

-Xms384M  
-Xmx384M  
-Djava.security.debug=all 为安全输出所有调试信息
-Djava.awt.headless=true 允许在没有图形显示安装软件情况下运行
-Duser.language=en  
-Dfile.encoding=UTF-8  
-Djava.net.preferIPv4Stack=true 使用IPv4而不是IPv6,有些BSD系统上一定要开
-enableassertions 启用assertion检查
-verbose:class  
-verbose:gc  

打开远程调试:

JAVA_OPTS="-Xdebug \
	-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"

通过JMX实现本地管理与监控:

CATALINA_OPTS = " - Dcom.sun.management.jmxremote=true \
	- Dcom.sun.management.jmxremote.ssl = false  \
	- Dcom.sun.management.jmxremote.authenticate = false"

如果要通过远程管理与监控:

CATALINA_OPTS = " - Dcom.sun.management.jmxremote.port = 8008 \
	- Dcom.sun.management.jmxremote.ssl = false  \
	- Dcom.sun.management.jmxremote.authenticate = false \
	- Dcom.sun.management.jmxremote.password.file=/path/to/pw/file"

防止因为服务器没有图形界面而出错:

-Djava.awt.headless=true

指定语言环境信息,可以直接在shell中完成:

$ LANG=en_US
$ catalina.sh start

全局的脚本配置

不推荐该方法。

$CATALINA_HOME/bin下创建脚本文件setenv。该文本会在Tomcat启动时被调用。

例:

#!/bin/bash

JVM_MAX_MEM=$(free -m|awk '/Mem/{print$2-1300}')
JVM_AGG_MAX_MEM=$(echo $JVM_MAX_MEM |awk '{print $0 * 0.7}')
JVM_AGG_MIN_MEM=$(echo $JVM_MAX_MEM |awk '{print $0 * 0.2}')
MY_IP=$(ifconfig eth0 |awk -F '[ |:]+' '/inet /{print $4}')

if [[ -z $JVM_MAX_MEM || $JVM_MAX_MEM -lt 1024 ]] ;then
	JVM_AGG_MAX_MEM=1024
	JVM_AGG_MIN_MEM=1024
fi

JAVA_OPTS="-server -XX:+AggressiveOpts -XX:+DoEscapeAnalysis \
                -Xmx${JVM_AGG_MAX_MEM}m \
                -Xms${JVM_AGG_MIN_MEM}m \
                -Xmn512m \
                -Xss512K \
                -XX:PermSize=128M \
                -XX:MaxPermSize=256M \
                -XX:SurvivorRatio=8 \
                -XX:+UseCompressedOops \
                -XX:+UseParNewGC \
                -XX:+UseConcMarkSweepGC \
                -XX:+CMSClassUnloadingEnabled \
                -XX:+UseCMSCompactAtFullCollection \
                -XX:CMSFullGCsBeforeCompaction=0 \
                -XX:+CMSParallelRemarkEnabled \
                -XX:+DisableExplicitGC \
                -XX:+UseCMSInitiatingOccupancyOnly \
                -XX:CMSInitiatingOccupancyFraction=75 \
                -XX:SoftRefLRUPolicyMSPerMB=0 \
				"

系统启动脚本

结合initd工具的系统服务脚本:

#!/bin/bash
# SCRIPT: tomcat
# AUTHOR: Ctrip SerweiXu xu.wei@ctrip.com
# DATE: 06-11-2013
# REV: 1.0
### BEGIN INIT INFO
# Provides: tomcat7
# Required-Start: $network $syslog
# Required-Stop: $network $syslog
# Default-Start:
# Default-Stop:
# Description: Release implementation for Servlet 2.5 and JSP 2.1
# Short-Description: start , stop and thread dump tomcat
# chkconfig: - 80 20
#
 
## Source function library.
. /etc/rc.d/init.d/functions
export JAVA_HOME=/usr/java/jdk1.7.0_51
export PATH=$JAVA_HOME/bin:$PATH
TOMCAT_HOME=/opt/app/tomcat
TOMCAT_USER=deploy
TOMCAT_LOG_LOCAL=/opt/logs/tomcat
SHUTDOWN_WAIT=3
DATESTAMP=`date "+%Y-%m-%d_%H-%M-%S"`

tomcat_pid=$( ps aux | grep -i tomcat|grep org.apache.catalina.startup.Bootstrap | grep -v grep | awk '{ print $2 }')

if [[ -r $TOMCAT_HOME/bin/setenv.sh ]];then
	. $TOMCAT_HOME/bin/setenv.sh
fi

[[ -z $USER ]] && USER=$(/usr/bin/id -un)

_start_action () {
	if [[ $USER == "root" ]];then
		/bin/su -p -s /bin/sh $TOMCAT_USER  $TOMCAT_HOME/bin/startup.sh
	elif [[ $USER == "deploy" ]];then
		/bin/sh $TOMCAT_HOME/bin/startup.sh
	else
		echo "tomcat only root or deploy run! ....."
		exit
	fi
}

_tdump_action () {
	[[ ! -z $1 ]] && $argv1 = $1
	if [[ $USER == "root" ]];then
        su -c "$JAVA_HOME/bin/jstack $argv1 $pid" $TOMCAT_USER  >${TOMCAT_LOG_LOCAL}/${DATESTAMP}_thread_dump_$pid.log 2>&1
	elif [[ $USER == "deploy" ]];then
        $JAVA_HOME/bin/jstack  $argv1 $pid  >${TOMCAT_LOG_LOCAL}/${DATESTAMP}_thread_dump_$pid.log 2>&1
	fi
}


tdump() {
    pid=${tomcat_pid}
    if [ -n "$pid" ]
    then
		_tdump_action 
        echo "The dump file is generated in the ${TOMCAT_LOG_LOCAL}/${DATESTAMP}_thread_dump_$pid.log ."
        grep "The -F option can be used" ${TOMCAT_LOG_LOCAL}/${DATESTAMP}_thread_dump_${pid}.log >> /dev/null 2>&1
        if [ $? -eq 0 ]; then
         echo "Trigger dump by focre mode!."
                  ForceFlag="-F"
		_tdump_action -F
        echo "The dump file is generated in the ${TOMCAT_LOG_LOCAL}/${DATESTAMP}_thread_dump_${pid}.log ."
        else
        echo ""
        fi
    else
                echo  'Tomcat is not running.'
    fi
}


 
start() {
  if [[ -f "/proc/${tomcat_pid}/statm" ]];then
    echo "Tomcat is already runnaing (pid: $tomcat_pid)"
	exit 0;
  fi
 
	_start_action
 
  return 0
}
 
stop() {
  if [[ -n "$tomcat_pid" ]];then
    /bin/sh $TOMCAT_HOME/bin/shutdown.sh
	sleep $SHUTDOWN_WAIT
  fi
    echo "Tomcat is stopped..."
  return 0
}
 
case $1 in
tdump)
 tdump
;;
start)
  start $2
;; 
stop)   
  stop
;; 
restart)
  stop
  sleep 3
  start
;;
status)
  if [ -f "/proc/$tomcat_pid/statm" ]
  then
    echo "tomcat (pid $tomcat_pid) is running..."
  else
    echo "tomcat is stopped"
  fi
;; 
  *)
        echo "Usage: $0 {start|stop|status|tdump|restart} [jvmsize]"
        exit 1
esac    
exit 0

管理领域、角色及用户

领域(Realm)

领域是角色、用户、密码三种信息的结合。Tomcat提供了几种默认的领域实现, 它们用不同的机制来存储这些信息:

  • UserDatabaseRealm:从内存中取得领域信息。只有在容器启动时从配置文件 $CATALINA_HOME/conf/tomcat-users.xml中读取领域信息,初始化到内存里。
  • JDBCRealm:领域信息在数据库里。server.xml中配置对应数据的连接参数。
  • JNDIRealm:领域信息在LDAP目录中。
  • JAASRealm:领域信息由Java验证与授权服务 (Java Authentication and Authorization Service)管理。

Tomcat容器自带的安全防护

  • 基本验证:当web.xmlauth-method值为BASIC,每次浏览器访问受保护资源, Tomcat会要求浏览器提供用户名和密码。(注意:这个并不是HTTP的基本验证)。
  • 摘要验证:当web.xmlauth-method值为DIGESTserver.xml中的Realm 设定也要更改。
  • 表单验证:提供一个登录页面。
  • Client-Cert验证:HTTPS实现,以浏览器的X.509数字证书认证。

控制会话

Tomcat支持可配置的会话管理器(Session Managers)。并且提供Session Store 支持存储与加载会话。

Session Manager在server.xml中配置:

<Manager className="som.manager.implementation.className 
	customAttribute1="some custom value"
	customAttribute2="some other custom value" />

StandardManager

如果没有指定会话管理器,那么默认的会话管理器是StandardManager。

会话信息会保存在磁盘上:

$CATALINA_HOME/work/Catalina/<hostname>/<webapp-name>/SESSIONS.ser

如果Tomcat非正常终止(如kill -9等),因为来没有存盘会话会丢失。

PersistentManager

在Tomcat 5中还是实现性的功能,PersistenManager把会话存入Sessions Store。 结合FileStore把会话存入文件,JDBCStore把会话存入数据库。

自动重新部署Servlet

开发过程中自动重部署修改过的Servlet是个方便的功能,但生产环境这样做开销太大了。 修改对应WebAppweb.xml文件的Context元素中设定reloadable属性并重启Tomcat。 这样也可以在Manager应用程序中手动重载工程。

Tomcat Admin webapp

$CATALINA_HOME/conf/tomcat-users.xml

<?xml version='1.0' encoding='utf-8'?>

	<role rolename="role1"/>

	<user username="admin" password="admin" 
		roles="admin-gui,manager-gui,manager-script,manager-jmx,manager-status" />
	
	<user username="role1" password="role1" roles="role1" />

</tomcat-users>

管理程序的入口为: http://localhost:8080/manager/status

开启JVM调试

JVM 激活远程调试的启动参数有JPDA_OPTS,CATALINA_OPTSJAVA_OPTS。 其中JAVA_OPTS是通常不建议使用的, 因为基于JAVA_OPTS的参数设定会暴露给所有的 JVM 应用, 而CATALINA_OPTS定义的设定值限制在Tomcat内。

使用JPDA_OPTS

CATALINA_HOME/bin目录下创建可执行脚本文件setenv.sh( Windows 创建setenv.bat), 加入内容:

Linux setenv.sh

export JPDA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"

Windows setenv.bat

set JPDA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"

当然上面的设置也可以直接放到catalina.sh(catalina.bat )内, 但是有个setevn.*额外的配置文件一直是最佳选择, tomcat会自动读取。

部署Java Web应用

Tomcat的配置把Web应用称为context。配置文件以XML文件的形式放对应的目录中:

$CATALINA_HOME/conf/[EngineName]/[HostName]

主机(Host)

Web应用必须属于一个主机,主机代表访问应用时完整的域名或是IP地址。 默认的主机名是localhost

server.xml中的Host节点配置主机:

<Host name="groovywigs.com" appBase="webapps" unpackWARs="true"
	autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
	<!-- 这里是groovywigs.com主目录的Context元素 -->
</Host>

<!-- 默认虚拟主机localhost -->
<Host name="localhost" appBase="webapps" unpackWARs="true"
	autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
	<!-- ... -->
</Host>
  • autoDeploy:热布署。
  • deployOnStartup:Tomcat启动时重新布署一次系统(不管是不是已经热布署过)。

autoDeploydeployOnStartup都打开时,可能会出现应用被重复布署两次的情况: 自动布署一次,重启时又布署一次。

以目录形式布署

配置可以放在两个地方:

  • $CATALINA_HOME/conf/server.xml中增加一个Context节点。
  • $CATALINA_HOME/conf/<EngineName>/<HostName>/目录中增加一个XML文件。

方法一:server.xml(推荐)

Host下添加一个Context节点:

<Host name="localhost" appBase="webapps"
	unpackWARs="true" autoDeploy="false"
	xmlValidation="false" xmlNamespaceAware="false">
	
	<Context docBase="my-webapp" path="/my-webapp" />

</Host>
  • docBase:程序文件在webapp下的目录。
  • path:URL中访问应用的位置。
映射应用的URL为ROOT

如果要把path映射为Root URL(/),不存在以下文件(如果有的话删除就可以):

$CATALINA_HOME/conf/<EngineName>/<HostName>/ROOT.xml

然后设置Contextpath""

<Host name="localhost" appBase="webapps"
	unpackWARs="true" autoDeploy="false"
	xmlValidation="false" xmlNamespaceAware="false">
	
	<Context docBase="my-webapp" path="" />

</Host>

方法二:Contex XML

不用完整的server.xml文件,只是其中Context段落。

$CATALINA_HOME/conf/<EngineName>/<HostName>/目录中增加一个XML文件。

例,$CATALINA_HOME/conf/<EngineName>/<HostName>/admin.xml

<Context docBase="${catalina.home}/server/webapps/admin" privileged="true"
	antiResourceLocking="false" antiJARLocking="false">
</Context>

另一个例子,如果程序在/opt/my-webapp/,映射到URL为my-webapp, 要指定配置文件名为:

$CATALINA_HOME/conf/<EngineName>/<HostName>/my-webapp.xml
<Context docBase="/opt/myweb-app">
</Context>

布署WAR文件

配置可以放在两个地方:

  • $CATALINA_HOME/conf/server.xml中增加一个Context节点。
  • $CATALINA_HOME/conf/<EngineName>/<HostName>/目录中增加一个XML文件。

方法一:server.xml(推荐)

Host下添加一个Context节点:

<Host name="localhost" appBase="webapps"
	unpackWARs="true" autoDeploy="false"
	xmlValidation="false" xmlNamespaceAware="false">
	
	<Context docBase="my-webapp.war" path="/my-webapp" />

</Host>
  • docBase:程序文件在webapp下的位置。
  • path:URL中访问应用的位置。
映射应用的URL为ROOT

如果要把path映射为Root URL(/),不存在以下文件(如果有的话删除就可以):

$CATALINA_HOME/conf/<EngineName>/<HostName>/ROOT.xml

然后设置Contextpath""

<Host name="localhost" appBase="webapps"
	unpackWARs="true" autoDeploy="false"
	xmlValidation="false" xmlNamespaceAware="false">
	
	<Context docBase="my-webapp.war" path="" />

</Host>

方法二:Contex XML

不用完整的server.xml文件,只是其中Context段落。

$CATALINA_HOME/conf/<EngineName>/<HostName>/目录中增加一个XML文件。 XML文件的文件名就是对应的URL路径,如果要对应URL根目录则文件名应该为ROOT

例,$CATALINA_HOME/conf/<EngineName>/<HostName>/admin.xml

<Context docBase="${catalina.home}/server/webapps/admin" privileged="true"
	antiResourceLocking="false" antiJARLocking="false">
</Context>

另一个例子,如果程序在/opt/my-webapp/,映射到URL为my-webapp, 要指定配置文件名为:

$CATALINA_HOME/conf/<EngineName>/<HostName>/my-webapp.xml
<Context docBase="/opt/myweb-app.war">
</Context>

配置服务:systemd

创建/etc/systemd/system/tomcat.service服务cd /etc/system/system

[Unit]
Description=ApacheTomcat Web Application Container
After=syslog.targetnetwork.target
[Service]
Type=forking
Environment=JAVA_HOME=/usr/local/jdk-11.0.1
Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid
Environment=CATALINA_HOME=/opt/tomcat
Environment=CATALINA_BASE=/opt/tomcat
Environment='CATALINA_OPTS=-Xms512M-Xmx1024M -server -XX:+UseParallelGC'
Environment='JAVA_OPTS=-Djava.awt.headless=true-Djava.security.egd=file:/dev/./urandom'
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/bin/kill-15 $MAINPID
User=tomcat
Group=tomcat
UMask=0007
RestartSec=10
Restart=always
[Install]
WantedBy=multi-user.target

启动tomcat服务:

systemctl daemon-reload
systemctl enable tomcat
systemctl start tomcat

常见错误排除

日志分析

  • 日志配置conf/logging.properties,调整各个类的输出级别。
  • Tomcat的系统日志输出在logs/catalina.out

调试HTTP交互

用telnet调试HTTP的302重定向请求:

$ telnet localhost 80

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0
HTTP/1.1 302 Moved Temporarily

Content-Type: text/html;charset=utf-8
Date: Thu, 27 Sep 2007 15:21:35 GMT
Location: http://localhost:8080/index.html
Server: Apache-Coyote/1.1
Connection: close

<html>
<head>
<title>Tomcat Error Report</title>
</head>
<body bgcolor="white">
<br><br>
<h1>HTTP Status 302 - Moved Temporarily</h1>
The requested resource (Moved Temporarily) has moved temporarily to a new location.
</body>
</html>
Connection closed by foreign host.

用telnet调试HTTP的200成功请求:

$ telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /index.html HTTP/1.0
HTTP/1.1 200 OK

Content-Type: text/html;charset=utf-8
Content-Length: 2836
Date: Thu, 27 Sep 2007 15:33:00 GMT
Server: Apache-Coyote/1.1
Last-Modified: Fri, 12 Oct 2001 22:36:50 GMT
ETag: "2836-1002926210000"

<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META NAME="GENERATOR" CONTENT="The vi editor from Unix">
<META NAME="Author" CONTENT="Ian Darwin">
<TITLE>Ian Darwin's Webserver On The Road</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="/stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="#c0d0e0">
<H1>Ian Darwin's Webserver On The Road</H1>
# Rest of the HTML not shown here...
</BODY></HTML>

使用netcat(即nc命令)模拟HTTP连接调试:

$ (echo GET / HTTP/1.0; echo "") | nc localhost 80
HTTP/1.1 302 Moved Temporarily
Content-Type: text/html;charset=utf-8
Date: Thu, 27 Sep 2007 15:21:47 GMT
Location: http://localhost:8080/index.html
Server: Apache-Coyote/1.1
Connection: close

<html>
<head>
<title>Tomcat Error Report</title>
</head>
<body bgcolor="white">
<br><br>
<h1>HTTP Status 302 - Moved Temporarily</h1>
The requested resource (Moved Temporarily) has moved temporarily to a new location.
</body>
</html>

用RequestDumperValue来调试

conf/server.xml中在HostContext里加打开RequestDumperValue调试:

<Valve className="org.apache.catalina.valves.RequestDumperValve" />

然后在ServerHostContext的日志文件中会有详细的信息。

每个请求的信息用一串等号分隔开来:

RequestDumperValve[/darwinsys]:
===============================================================
RequestDumperValve[/darwinsys]: REQUEST URI =/darwinsys/index.jsp
RequestDumperValve[/darwinsys]: authType=null
RequestDumperValve[/darwinsys]: characterEncoding=null
RequestDumperValve[/darwinsys]: contentLength=-1
RequestDumperValve[/darwinsys]: contentType=null
RequestDumperValve[/darwinsys]: contextPath=/darwinsys
RequestDumperValve[/darwinsys]:
cookie=JSESSIONID=C04FE083F247D0C7F24174AA8B78B526
RequestDumperValve[/darwinsys]: header=connection=Keep-Alive
RequestDumperValve[/darwinsys]: header=user-agent=Mozilla/5.0 (compatible;
Konqueror/2.2.2; OpenBSD 3.1; X11; i386)
RequestDumperValve[/darwinsys]: header=accept=text/*, image/jpeg, image/png,
image/*, */*
RequestDumperValve[/darwinsys]: header=accept-encoding=x-gzip, gzip, identity
RequestDumperValve[/darwinsys]: header=accept-charset=Any, utf-8, *
RequestDumperValve[/darwinsys]: header=accept-language=en
RequestDumperValve[/darwinsys]: header=host=localhost:8080
RequestDumperValve[/darwinsys]:
header=cookie=JSESSIONID=C04FE083F247D0C7F24174AA8B78B526
RequestDumperValve[/darwinsys]: header=authorization=Basic
aWFkbWluOmZyZWRvbmlh
RequestDumperValve[/darwinsys]: locale=en
RequestDumperValve[/darwinsys]: method=GET
RequestDumperValve[/darwinsys]: pathInfo=null
RequestDumperValve[/darwinsys]: protocol=HTTP/1.1
RequestDumperValve[/darwinsys]: queryString=null
RequestDumperValve[/darwinsys]: remoteAddr=127.0.0.1
RequestDumperValve[/darwinsys]: remoteHost=127.0.0.1
RequestDumperValve[/darwinsys]: remoteUser=null
RequestDumperValve[/darwinsys]: requestedSessionId=C04FE083F247D0C7F24174AA8B78B526
RequestDumperValve[/darwinsys]: scheme=http
RequestDumperValve[/darwinsys]: serverName=localhost
RequestDumperValve[/darwinsys]: serverPort=8080
RequestDumperValve[/darwinsys]: servletPath=null
RequestDumperValve[/darwinsys]: isSecure=false
RequestDumperValve[/darwinsys]: ------------------------------------------------- --------
------
RequestDumperValve[/darwinsys]: ------------------------------------------------- --------
------
RequestDumperValve[/darwinsys]: authType=null
RequestDumperValve[/darwinsys]: contentLength=-1
RequestDumperValve[/darwinsys]: contentType=text/html;ISO-8859-1
RequestDumperValve[/darwinsys]:
cookie=JSESSIONID=3042D12AD0B976B9EB83F3ECDDFD095F; domain=null; path=/darwinsys
RequestDumperValve[/darwinsys]: header=Content-Type=text/html;ISO-8859-1
RequestDumperValve[/darwinsys]: header=Connection-Type=chunked
RequestDumperValve[/darwinsys]: header=Date=Thu, 27 Sep 2007 17:11:31 GMT
RequestDumperValve[/darwinsys]: header=Server=Apache-Coyote/1.1
RequestDumperValve[/darwinsys]: header=Set-Cookie=text/html;ISO-8859- 1
RequestDumperValve[/darwinsys]: header=Set-
Cookie=JSESSIONID=3042D12AD0B976B9EB83F3ECDDFD095F; Path=/darwinsys
RequestDumperValve[/darwinsys]: header=Date=Thu, 27 Sep 2007 17:11:31 GMT
RequestDumperValve[/darwinsys]: header=Server=Apache-Coyote/1.1
RequestDumperValve[/darwinsys]: message=null
RequestDumperValve[/darwinsys]: remoteUser=null
RequestDumperValve[/darwinsys]: status=200
RequestDumperValve[/darwinsys]:
============================================================

Tomcat无法退出

查找Tomcat的进程:

ps auwwx | grep java | grep org.apache.catalina.startup.Bootstrap

-SIGQUIT退出进程,这样执行线程的信息记录到catalina.out里。 比如,当查到进程号是456时:

kill -SIGQUIT 456

查看catalina.out里的线程信息:

Sep 27, 2007 9:57:36 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1286 ms
Full thread dump Java HotSpot(TM) 64-Bit Server VM (1.6.0_b105 mixed mode, sharing):

"TP-Monitor" daemon prio=1 tid=0x00002aaaaec5d700 nid=0x70b6 in Object.wait()
[0x00000000412d3000..0x00000000412d3d00]
	at java.lang.Object.wait(Native Method) - waiting on <0x00002aff518c50b8> 
		(a org.apache.tomcat.util.threads. ThreadPool$MonitorRunnable)
	at org.apache.tomcat.util.threads.ThreadPool$MonitorRunnable.run
		(ThreadPool.java: 561) - locked <0x00002aff518c50b8> 
		(a org.apache.tomcat.util.threads.  ThreadPool$MonitorRunnable)
	at java.lang.Thread.run(Thread.java:595) "TP-Processor4" daemon prio=1 
		tid=0x00002aaaae0a3e80 nid=0x70b5 runnable 
		[0x00000000411d2000..0x00000000411d2c80]
	at java.net.PlainSocketImpl.socketAccept(Native Method)
	at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:384) 
		- locked <0x00002aff51883d28> (a java.net.SocksSocketImpl)
	at java.net.ServerSocket.implAccept(ServerSocket.java:450)
	at java.net.ServerSocket.accept(ServerSocket.java:421)
	at org.apache.jk.common.ChannelSocket.accept(ChannelSocket.java:306)
	at org.apache.jk.common.ChannelSocket.acceptConnections
		(ChannelSocket.java:660)
	at org.apache.jk.common.ChannelSocket$SocketAcceptor.runIt
		(ChannelSocket.java: 870)
	at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run
		(ThreadPool.java: 686)
	at java.lang.Thread.run(Thread.java:595) 
		"TP-Processor3" daemon prio=1 tid=0x00002aaaae0a3090 
		nid=0x70b4 in Object.wait() [0x00000000410d1000..0x00000000410d1c00]
	at java.lang.Object.wait(Native Method) - waiting on <0x00002aff518c4538> 
		(a org.apache.tomcat.util.threads.  ThreadPool$ControlRunnable)
	at java.lang.Object.wait(Object.java:474)
	at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run
		(ThreadPool.java: 658) - locked <0x00002aff518c4538> 
		(a org.apache.tomcat.util.threads. ThreadPool$ControlRunnable) 
		at java.lang.Thread.run(Thread.java:595)

检查这些线程有哪些是创建的非守护线程(non-daemon), 只有守护线程能在Tomcat关闭时自动退出。非守护线程在没有执行完之前是不会退出的。