tomcat 在liunx中shutdown后进程仍然存在的两种实用解决办法
方法一:
查找tomcat 进程并 kill
ps -elf | grep ${ctompath} | grep -v grep | awk '{print $4}' | xargs kill -9
此处可写成脚本,可参考我之前的脚本 https://www.cnblogs.com/shenjianxin/p/9263765.html
方法二:
修改$TOMCAT_HOME/bin/catalina.sh文件,在PRGDIR下面一行添加CATALINA_PID参数行,修改完成后应该跟下面相同。
# Get standard environment variables PRGDIR=`dirname "$PRG"` CATALINA_PID=$PRGDIR/CATALINA_PID
也可以自定义pid路径,例如“CATALINA_PID=/var/run/tomcat.pid”
要杀死进程需要添加参数 -force ./shutdown.sh -force 或者 ./catalina.sh stop -force
源码分析:(以下出处 https://blog.csdn.net/hxyerui/article/details/52181884)
最近我们在使用Jenkins 自动化部署项目时,在生产 liunx 环境下,使用脚本 shutdown.sh 停止 tomcat 服务,然后再 start 之后发现应用无法访问了,后台查看 tomcat 进程是发现有个 2 个 tomcat 进程,说明之前的 shutdown 并没有完全停掉 tomcat 进程。那怎么样 tomcat 使用 shutdown 之后立马关掉其进程呢? 经查资料发现在 shutdown.sh 脚本之后有条命令是这样的:
exec "$PRGDIR"/"$EXECUTABLE" stop "$@"
这个就是停止tomcat 服务的命令,我们只需要加一个 -force 就可以在 shutdown 时强制关闭 tomcat 进程
exec "$PRGDIR"/"$EXECUTABLE" stop -force "$@"
但光这样还是不行,再shutdown 时报错:
Kill failed: $CATALINA_PID not set
查找失败原因:
- + FORCE=1
- + '[' '!' -z ''']'
- + /usr/java/jdk1.6.0_38/bin/java -server -Xms2048m -Xmx2048m -Xmn768m -XX:PermSize=128m -XX:MaxPermSize=256m -XX:+UseParallelOldGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/xrltest1/tomcat/dumpfile/heap.bin -Xloggc:/home/xrltest1/tomcat/logs/gc.log -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/home/xrltest1/tomcat/endorsed -classpath /home/xrltest1/tomcat/bin/bootstrap.jar -Dcatalina.base=/home/xrltest1/tomcat -Dcatalina.home=/home/xrltest1/tomcat -Djava.io.tmpdir=/home/xrltest1/tomcat/temp org.apache.catalina.startup.Bootstrap stop
- 2015-3-21 11:59:53 org.apache.catalina.startup.Catalina stopServer
- 严重: Catalina.stop:
- java.net.ConnectException: Connection refused
- at java.net.PlainSocketImpl.socketConnect(Native Method)
- at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
- at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
- at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
- at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
- at java.net.Socket.connect(Socket.java:529)
- at java.net.Socket.connect(Socket.java:478)
- at java.net.Socket.<init>(Socket.java:375)
- at java.net.Socket.<init>(Socket.java:189)
- at org.apache.catalina.startup.Catalina.stopServer(Catalina.java:422)
- at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
- at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
- at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
- at java.lang.reflect.Method.invoke(Method.java:597)
- at org.apache.catalina.startup.Bootstrap.stopServer(Bootstrap.java:338)
- at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:416)
- + '[' '!' -z ''']'
- + '[' 1 -eq 1 ']'
- + '[' -z ''']'
- + echo 'Kill failed: $CATALINA_PID not set'
- Kill failed: $CATALINA_PID not set
从中可以看到,首先是执行java 命令失败,没有停止 tomcat,然后执行 -force 模块的命令,但是却没有找到 $CATALINA_PID 设定的进程号,我们先不去关注 java 执行 stop 命令为什么报错,而是看看为什么加了 -force 参数也不起作用了
为什么没有$CATALINA_PID?接下来就带大家一探究竟
首先,我们将涉及到$CATALINA_PID 变量的代码全部提取出来:
第一处:
#以下这句判断设置的 $CATALINA_PID 变量如果不存在,则显示 "Using CATALINA_PID:
- $CATALINA_PID",如果存在则不显示
- if [! -z "$CATALINA_PID"]; then
- echo "Using CATALINA_PID: $CATALINA_PID"
- fi
- fi
#貌似只是判断 $CATALINA_PID 是否是空字符,其他什么都没有做
第二处:(涉及到start 参数的模块内的 $CATALINA_PID 变量,只是提出来分析,其实不对 stop 模块有影响)
#这个是start模块内的代码 "$_RUNJAVA" "$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \ -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ -Dcatalina.base="$CATALINA_BASE" \ -Dcatalina.home="$CATALINA_HOME" \ -Djava.io.tmpdir="$CATALINA_TMPDIR" \ org.apache.catalina.startup.Bootstrap "$@" start \ >> "$CATALINA_OUT" 2>&1 & #从&可以看出启动的命令在后台启动 fi if [! -z "$CATALINA_PID" ]; then #判断CATALINA_PID如果不是空字符,则将Shell最后运行的后台Process的PID 传给$CATALINA_PID echo $! > "$CATALINA_PID" #在使用命令运行进程至后台时,可以使用$!抓取前面启动运行在后台进程的进程号 fi fi #上面语句是tomcat在启动时,会将$CATALINA_PID写入PID进程号
第三处:
#一下语句都出现在stop模块内 if [! -z "$CATALINA_PID" ]; then #$CATALINA_PID文件不是非空 if [-f "$CATALINA_PID" ]; then if [-s "$CATALINA_PID" ]; then kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1 #kill -0 pid 不发送任何信号,但是系统会进行错误检查。 if [$? -gt 0]; then echo "PID file found but no matching process was found. Stop aborted." exit 1 fi else echo "PID file is empty and has been ignored." fi else echo "\$CATALINA_PID was set but the specified file does not exist. Is Tomcat running? Stop aborted." exit 1 fi fi
#如果发现 $CATALINA_PID 则发送一个模拟结束进程的型号,如果返回值为 0,则正常,其他则异常,做退出操作
#从上面的 sh -x 输出的信息对应的是:'[' '!' -z ''']' 可以看出 $CATALINA_PID 变量没有设定,所以这个判断直接就结束了,什么都没做
第四处:
#下面的代码紧接着stop的正常停止代码下 if [! -z "$CATALINA_PID" ]; then #如果$CATALINA_PID不为空 if [-f "$CATALINA_PID" ]; then #而且还是普通文件 while [$SLEEP -ge 0]; do #而且$SLEEP还大于0 kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1 #则测试下tomcat能不能被关闭 if [$? -gt 0]; then #剩下的参数还有,则清空$CATALINA_PID rm -f "$CATALINA_PID" >/dev/null 2>&1 if [$? != 0]; then if [-w "$CATALINA_PID" ]; then cat /dev/null > "$CATALINA_PID" else echo "Tomcat stopped but the PID file could not be removed or cleared." fi fi break fi if [$SLEEP -gt 0]; then sleep 1 fi if [$SLEEP -eq 0]; then if [$FORCE -eq 0]; then echo "Tomcat did not stop in time. PID file was not removed." fi fi SLEEP=`expr $SLEEP - 1 ` done fi fi #上段语句主要是判断tomcat是否被关闭 #核心语句还是:kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1 #while语句做sleep使用,用于清空$CATALINA_PID #但是[! -z "$CATALINA_PID"]导致这段语句什么都没做啊!!!!!
第五处:
#这段代码就是涉及-force的核心代码了 if [$FORCE -eq 1]; then if [-z "$CATALINA_PID" ]; then echo "Kill failed: \$CATALINA_PID not set" else if [-f "$CATALINA_PID" ]; then PID=`cat "$CATALINA_PID"` echo "Killing Tomcat with the PID: $PID" kill -9 $PID #强制执行的核心命令 rm -f "$CATALINA_PID" >/dev/null 2>&1 if [$? != 0]; then echo "Tomcat was killed but the PID file could not be removed." fi fi fi fi #从中就可以看出,sh -x输出的Kill failed: $CATALINA_PID not set是怎么来的
总结下,$CATALINA_PID 在整个代码中的作用:
1. 在 tomcat 启动时会写入 $CATALINA_PID,但是假设我们的环境是多 tomcat 项目或 $CATALINA_PID 为空
2.stop 代码中,检查 $CATALINA_PID 是否为空字符,是的话什么都不做
3.sstop 代码中,检查 $CATALINA_PID 是否为空字符,是的话什么都不做
4.force 代码中,检查 $CATALINA_PID 是否为空字符,是的话就报错
也就是说只要没有保存中这个项目的PID,那么正常 stop 停止不了,-force 也是没有用的。
那么该如何解决呢?直接给$CATALINA_PID 付 PID 的值,那么看结果:
+ FORCE=1 + '[' '!' -z 12031 ']' + '[' -f 12031 ']' + echo '$CATALINA_PID was set but the specified file does not exist. Is Tomcat running? Stop aborted.' $CATALINA_PID was set but the specified file does not exist. Is Tomcat running? Stop aborted.
涉及到的代码:
if [-f "$CATALINA_PID" ]; then if [-s "$CATALINA_PID" ]; then kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1 #kill -0 pid 不发送任何信号,但是系统会进行错误检查。 if [$? -gt 0]; then echo "PID file found but no matching process was found. Stop aborted." exit 1 fi else echo "PID file is empty and has been ignored." fi else echo "\$CATALINA_PID was set but the specified file does not exist. Is Tomcat running? Stop aborted." exit 1 #$CATALINA_PID不是个文件,所以不好意思,我退出了....
原来$CATALINA_PID 还必须是个文件,那么我们可不可以建这么一个文件呢?
在catalina.sh 脚本的代码前,加入以下语句:
######################################################################################### if [-z "$CATALINA_PID"]; then CATALINA_PID=$PRGDIR/CATALINA_PID cat $CATALINA_PID fi
######################################################################################
结果:tomcat 停止执行成功,完成结束进程任务
总结:
在我看到的很到部署tomcat 的文章中,还没发现有关于设置 $CATALINA_PID 文件路径的提示(也许是我看到的少),但是此处我要建议小伙伴们,在写 tomcat 启动、停止、重启的脚本的时候,一定要注意这个变量。