Cabbage Java study room covers core knowledge

Tomcat lightweight application server principle probe secret architecture analysis part Tomcat lightweight application server principle probe secret one key stop start part Tomcat lightweight application server principle probe secret class loader part Tomcat lightweight application server principle probe secret asynchronous communication part

Before SpringBoot embedded Tomcat, we used to type the project as War and put it in the Webapp directory of Tomcat, then run the start.sh command on Linux, and run the start.sh command on Windows. Run the start.bat command to start up and access the page. If you only need to run shutdown.sh or shutdown.bat to stop the program, how can Tomcat start all containers with a single command?

1. start.sh

Start. sh is the same as start.bat, so we will analyze the contents of start.sh:

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

# resolve links - $0 may be a softlink
# PRG是脚本路径,如果当前脚本文件为软连接,则会解析出PRG真正文件所在的路径
PRG="$0"

while [ -h "$PRG" ] ; do # 判断是否为软连接
  ls=`ls -ld "$PRG"`   # 如果是软连接,输出中含有lin -> source的字符串
  link=`expr "$ls" : '.*-> \(.*\)$'` # 模式匹配出源文件的路径
  if expr "$link" : '/.*' > /dev/null; then # 正则匹配 /.* 这里expr会输出匹配个数,如果不为0,则说明$link包含目录
    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

# 执行catalina.sh的start命令
exec "$PRGDIR"/"$EXECUTABLE" start "$@"
Copy the code

In fact, the above simple to do two things:

  1. Get the actual path to the script;
  2. performcatalina.shThe start command;

The shutdown.sh command is the same as the start.sh command, except that it is followed by the stop command catalina.sh.

2. catalina.sh

The important steps in the catalina.sh script are as follows:

  1. Set two important environment variables, CATALINA_HOME and CATALINA_BASE:
PRGDIR=`dirname "$PRG"`

[ -z "$CATALINA_HOME" ] && CATALINA_HOME=`cd "$PRGDIR/.." >/dev/null; pwd`

[ -z "$CATALINA_BASE" ] && CATALINA_BASE="$CATALINA_HOME"
Copy the code
  1. Set the CLASSPATH variable. Note that there is no setenv.sh file by default. You can create your own file and add parameters:
CLASSPATH=

if [ -r "$CATALINA_BASE/bin/setenv.sh" ]; then
  . "$CATALINA_BASE/bin/setenv.sh"
elif [ -r "$CATALINA_HOME/bin/setenv.sh" ]; then
  . "$CATALINA_HOME/bin/setenv.sh"
fi
Copy the code
  1. Pass the bootstrap.jar as the CLASSPATH variable:
if [ ! -z "$CLASSPATH" ] ; then
  CLASSPATH="$CLASSPATH":
fi
CLASSPATH="$CLASSPATH""$CATALINA_HOME"/bin/bootstrap.jar

if [ -z "$CATALINA_OUT" ] ; then
  CATALINA_OUT="$CATALINA_BASE"/logs/catalina.out
fi
Copy the code
  1. To execute the script arguments, execute the main() method of the bootstrap class in bootstrap.jar, passing in the start argument:
    shift
    eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
      -D$ENDORSED_PROP="\"$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
Copy the code

In the script above, we can see that the final execution is from the main() method of Bootstrap, so we open the Tomcat source code and go to the Bootstrap class to see what it does.

3. Bootstrap

As an entry class for Tomcat, let’s first take a look at what’s done in Bootstrap. Only the important code in the main() method is posted here.

// Initialize the classloader and load the Catalina file into memory bootstrap.init(); String command = "start"; if (args.length > 0) { command = args[args.length - 1]; } if (command.equals("startd")) { args[args.length - 1] = "start"; // Call catalina.java's load method daemon.load(args); // Call catalina.java's start daemon.start(); } else if (command.equals("stopd")) { args[args.length - 1] = "stop"; // Call catalina.java stop daemon.stop(); } else if (command.equals("start")) { daemon.setAwait(true); daemon.load(args); daemon.start(); if (null == daemon.getServer()) { System.exit(1); } } 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."); }Copy the code

Here, different methods of Catalina are called depending on the commands passed in the script. Since we mainly analyze how Tomcat does one-click start and stop, we mainly analyze Catalina’s start() method.

In Catalina’s start() method we see:

getServer().start();
Copy the code

Lifecycle’s methods are listed as follows:

public interface Lifecycle {

    public void addLifecycleListener(LifecycleListener listener);

    public LifecycleListener[] findLifecycleListeners();

    public void removeLifecycleListener(LifecycleListener listener);

    public void init() throws LifecycleException;

    public void start() throws LifecycleException;

    public void stop() throws LifecycleException;

    public void destroy() throws LifecycleException;

    public LifecycleState getState();

    public String getStateName();

    public interface SingleUse {
    
    }
    
}
Copy the code

When we look at its implementation classes, we see that all the components in the overall architecture we talked about earlier implement this class.

The LifecycleBase subclass implements start(), init(), stop(), and startInternal(), initInternal(), and stopInternal() methods. If we were familiar with design patterns, we would have thought that we were using the template design pattern, abstracting the public code of all subclasses, and then redefining an internal abstract method whose subclasses implement their own customized operations.

We find that the first layer in server.xml is also Server, and then the first one started in Catalina’s satrt() method is Server.

At this point we should understand how Tomcat can be started and stopped with one click. Load the widget first and then the large component, and load the inner component first and then the outer component. Through the hierarchy, load priority. The layer of iteration is started, and the layer of iteration is stopped.

Tomcat lightweight application server principle probe secret architecture analysis part Tomcat lightweight application server principle probe secret one key stop start part Tomcat lightweight application server principle probe secret class loader part Tomcat lightweight application server principle probe secret asynchronous communication part