Tomcat7.0源码分析——启动与停止服务原理

Posted 泰山不老生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat7.0源码分析——启动与停止服务原理相关的知识,希望对你有一定的参考价值。

前言

  熟悉Tomcat的工程师们,肯定都知道Tomcat是如何启动与停止的。对于startup.sh、startup.bat、shutdown.sh、shutdown.bat等脚本或者批处理命令,大家一定知道改如何使用它,但是它们究竟是如何实现的,尤其是shutdown.sh脚本(或者shutdown.bat)究竟是如何和Tomcat进程通信的呢?本文将通过对Tomcat7.0的源码阅读,深入剖析这一过程。

  由于在生产环境中,Tomcat一般部署在Linux系统下,所以本文将以startup.sh和shutdown.sh等shell脚本为准,对Tomcat的启动与停止进行分析。

启动过程分析

  我们启动Tomcat的命令如下:

sh startup.sh

所以,将从shell脚本startup.sh开始分析Tomcat的启动过程。startup.sh的脚本代码见代码清单1。

代码清单1

os400=false
case "`uname`" in
OS400*) os400=true;;
esac

# resolve links - $0 may be a softlink
PRG="$0"

while [ -h "$PRG" ] ; do
  ls=`ls -ld "$PRG"`
  link=`expr "$ls" : '.*-> \\(.*\\)$'`
  if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi
done

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" start "$@"

代码清单1中有两个主要的变量,分别是:

  • PRGDIR:当前shell脚本所在的路径;
  • EXECUTABLE:脚本catalina.sh。

根据最后一行代码:exec "$PRGDIR"/"$EXECUTABLE" start "$@",我们知道执行了shell脚本catalina.sh,并且传递参数start。catalina.sh中接收到start参数后的执行的脚本分支见代码清单2。

代码清单2

  elif [ "$1" = "start" ] ; then

  # 此处省略参数校验的脚本

  shift
  touch "$CATALINA_OUT"
  if [ "$1" = "-security" ] ; then
    if [ $have_tty -eq 1 ]; then
      echo "Using Security Manager"
    fi
    shift
    eval "\\"$_RUNJAVA\\"" "\\"$LOGGING_CONFIG\\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \\
      -Djava.endorsed.dirs="\\"$JAVA_ENDORSED_DIRS\\"" -classpath "\\"$CLASSPATH\\"" \\
      -Djava.security.manager \\
      -Djava.security.policy=="\\"$CATALINA_BASE/conf/catalina.policy\\"" \\
      -Dcatalina.base="\\"$CATALINA_BASE\\"" \\
      -Dcatalina.home="\\"$CATALINA_HOME\\"" \\
      -Djava.io.tmpdir="\\"$CATALINA_TMPDIR\\"" \\
      org.apache.catalina.startup.Bootstrap "$@" start \\
      >> "$CATALINA_OUT" 2>&1 "&"

  else
    eval "\\"$_RUNJAVA\\"" "\\"$LOGGING_CONFIG\\"" $LOGGING_MANAGER $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
    echo $! > "$CATALINA_PID"
  fi

  echo "Tomcat started."

从代码清单2可以看出,最终使用java命令执行了org.apache.catalina.startup.Bootstrap类中的main方法,参数也是start。Bootstrap的main方法的实现见代码清单3。

代码清单3

    /**
     * Main method, used for testing only.
     *
     * @param args Command line arguments to be processed
     */
    public static void main(String args[]) {

        if (daemon == null) {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable t) {
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        }

        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            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 {
                log.warn("Bootstrap: command \\"" + command + "\\" does not exist.");
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

    }

从代码清单3可以看出,当传递参数start的时候,command等于start,此时main方法的执行步骤如下:

步骤一 初始化Bootstrap

  Bootstrap的init方法(见代码清单4)的执行步骤如下:

  1. 设置Catalina路径,默认为Tomcat的根目录;
  2. 初始化Tomcat的类加载器,并设置线程上下文类加载器(具体实现细节,读者可以参考《Tomcat7.0源码分析——类加载体系》一文);
  3. 用反射实例化org.apache.catalina.startup.Catalina对象,并且使用反射调用其setParentClassLoader方法,给Catalina对象设置Tomcat类加载体系的顶级加载器(Java自带的三种类加载器除外)。