Linux Tomcat 强制停止

需求是:Linux下面执行Tomcat下面的shutdown.sh命令,Tomcat进程都会留在那边,如果在执行startup.sh启动的话,那么就会出现多个进程在那边,会导致资源消耗得太多。

有一种方法是通过手动查找进程的方法,ps -ef|grep java,然后获得进程ID,执行kill -9 进程ID,停止进程的运行。

例如,上面截图里面就有两个/uidcloud_Hcljfl_Test/gzhTomcat进程

如果需要停止则需要执行,kill -9 313 13236

这种方法太烦锁,可以通过修改catalina.sh文件,这样在执行shutdown.sh之后,如果没有关闭掉,则会被强制关闭。

 

1、 修shutdown.sh文件,增加几行脚本,主要是记录tomcat的pid,如下:
#设置记录CATALINA_PID。

#该设置会在启动时候bin下新建一个CATALINA_PID文件

#关闭时候从CATALINA_PID文件找到pid,kill。。。

#同时删除CATALINA_PID文件

在PRGDIR=`dirname "$PRG"`后面加上:

if [ -z "$CATALINA_PID" ]; then

      CATALINA_PID=$PRGDIR/CATALINA_PID

fi

第二步: shutdown.sh文件,在最后一行加上-force:
 

if $os400; then
  # -x will Only work on the os400 if the files are:
  # 1. owned by the user
  # 2. owned by the PRIMARY group of the user
  # this will not work if the user belongs in secondary groups
  eval
else
  if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
    echo "Cannot find $PRGDIR/$EXECUTABLE"
    echo "The file is absent or does not have execute permission"
    echo "This file is needed to run this program"
    exit 1
  fi
fi
if [ -z "$CATALINA_PID" ]; then
    CATALINA_PID=/home/logs/tomcat1.pid
    cat $CATALINA_PID
fi

exec "$PRGDIR"/"$EXECUTABLE" stop -force  "$@"

 

 

原因分析
停止Tomcat原理分析
我们先来看看tomcat实现关闭的原理是什么?如下为shutdown.sh脚本内容:

PRGDIR=`dirname "$PRG"`
EXECUTABLE=catalina.sh

# Check that target executable exists
if $os400; then
  # -x will Only work on the os400 if the files are:
  # 1. owned by the user
  # 2. owned by the PRIMARY group of the user
  # this will not work if the user belongs in secondary groups
  eval
else
  if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
    echo "Cannot find $PRGDIR/$EXECUTABLE"
    echo "The file is absent or does not have execute permission"
    echo "This file is needed to run this program"
    exit 1
  fi
fi

exec "$PRGDIR"/"$EXECUTABLE" stop "$@"
显然,shutdown.sh只是一个执行入口,真正执行关闭操作是在catalina.sh中实现的,继续查看catalina.sh脚本内容,在其中关于调用stop方法的地方可以看到如下信息:

eval "\"$_RUNJAVA\"" $LOGGING_MANAGER $JAVA_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 "$@" stop
# stop failed. Shutdown port disabled? Try a normal kill.
  if [ $? != 0 ]; then
    if [ ! -z "$CATALINA_PID" ]; then
      echo "The stop command failed. Attempting to signal the process to stop through OS signal."
      kill -15 `cat "$CATALINA_PID"` >/dev/null 2>&1
    fi
  fi
首先需要调用Tomcat的Bootstrap类,然后再通过kill命名停止Tomcat进程。但是注意 到在这里使用kill命令发送的信号为SIGTERM(15),也就是说有可能不能停止Tomcat进程(如:进程未释放系统资源)。

下面先追踪一下Bootstrap类的实现:

if (command.equals("startd")) {
    args[args.length - 1] = "start";
    daemon.load(args);
    daemon.start();
} else if (command.equals("stopd")) {
    args[args.length - 1] = "stop";
    daemon.stop();
} else if (command.equals("start")) {
    daemon.setAwait(true);
    daemon.load(args);
    daemon.start();
} else if (command.equals("stop")) {
    daemon.stopServer(args);
} else if (command.equals("configtest")) {
    daemon.load(args);
if (null==daemon.getServer()) {
    System.exit(1);
}
    System.exit(0);
} else {
    log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
在Bootstrap的main方法中会根据在catalina.sh脚本传递的不同参数(start,stop)执行不同的方法。其中,当参数为stop时会调用stopServer()方法。

/**
 * Stop the standalone server.
 * @param arguments Command line arguments
 * @throws Exception Fatal stop error
 */
public void stopServer(String[] arguments)
    throws Exception {

    Object param[];
    Class<?> paramTypes[];
    if (arguments==null || arguments.length==0) {
        paramTypes = null;
        param = null;
    } else {
        paramTypes = new Class[1];
        paramTypes[0] = arguments.getClass();
        param = new Object[1];
        param[0] = arguments;
    }
    Method method =
    catalinaDaemon.getClass().getMethod("stopServer", paramTypes);
    method.invoke(catalinaDaemon, param);
}
实际上,最终的停止服务操作是通过反射方式执行了Catalina类中的stopServer()方法,如下所示:

public void stopServer(String[] arguments) {

    if (arguments != null) {
        arguments(arguments);
    }
    Server s = getServer();
    if (s == null) {
        // Create and execute our Digester
        Digester digester = createStopDigester();
        File file = configFile();
        try (FileInputStream fis = new FileInputStream(file)) {
            InputSource is =
            new InputSource(file.toURI().toURL().toString());
            is.setByteStream(fis);
            digester.push(this);
            digester.parse(is);
        } catch (Exception e) {
            log.error("Catalina.stop: ", e);
            System.exit(1);
        }
    } else {
        // Server object already present. Must be running as a service
        try {
            s.stop();
        } catch (LifecycleException e) {
            log.error("Catalina.stop: ", e);
        }
        return;
    }

    // Stop the existing server
    s = getServer();
    if (s.getPort()>0) {
        try (Socket socket = new Socket(s.getAddress(), s.getPort());
            OutputStream stream = socket.getOutputStream()) {
            String shutdown = s.getShutdown();
            for (int i = 0; i < shutdown.length(); i++) {
                stream.write(shutdown.charAt(i));
            }
            stream.flush();
        } catch (ConnectException ce) {
            log.error(sm.getString("catalina.stopServer.connectException",
                                       s.getAddress(),
                                       String.valueOf(s.getPort())));
            log.error("Catalina.stop: ", ce);
            System.exit(1);
        } catch (IOException e) {
            log.error("Catalina.stop: ", e);
            System.exit(1);
        }
    } else {
        log.error(sm.getString("catalina.stopServer"));
        System.exit(1);
    }
}
如上所示,Tomcat进程的关闭操作需要做2件事:
第一:调用Bootstrap类的方法释放Tomcat进程所占用的资源。
第二:使用kill命令停止Tomcat进程:kill -15 <tomcat_process_id>。

为什么停止Tomcat之后进程依然存在
Tomcat是一个Servlet容器,用于部署Serlvet程序(我们通常写的各种Java Web应用本质上就是一个Servlet程序)。也就说,在停止Tomcat时不仅仅需要释放Tomcat进程本身所占用的资源,还需要释放Serlvet程序所占用的资源。而出现“停止Tomcat之后进程依然存在”这种现象的主要原因就是:我们自己写的Java Web应用在Tomcat容器停止时没有正常释放所占用的系统资源,比如:线程池未关闭,输入输出流未关闭等等。我在实际开发中就曾遇到因Kafka客户端未关闭到导致Tomcat无法正常停止的情况。然而,这却是很多做Web应用开发的程序员未引起注意的地方。往往都是不能正常关闭就直接强制杀死进程,当然达到了目的,但这并不是一个很好的做法。  

 

解决方案
我们必须确保在容器退出时正确地释放相应资源,如:实现ServletContextListener监听器接口,在contextDestroyed()方法中执行相应的关闭操作。

public class ResListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent sce) {
        //TODO:初始化资源
    }

    // 释放资源,否则容器无法正常关闭
    public void contextDestroyed(ServletContextEvent sce) {
        //TODO:释放资源
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

福海鑫森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值