前言

  熟悉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的命令如下:

  1. sh startup.sh

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

代码清单1

  1. os400=false
  2. case "`uname`" in
  3. OS400*) os400=true;;
  4. esac
  5.  
  6. # resolve links - $ may be a softlink
  7. PRG="$0"
  8.  
  9. while [ -h "$PRG" ] ; do
  10. ls=`ls -ld "$PRG"`
  11. link=`expr "$ls" : '.*-> \(.*\)$'`
  12. if expr "$link" : '/.*' > /dev/null; then
  13. PRG="$link"
  14. else
  15. PRG=`dirname "$PRG"`/"$link"
  16. fi
  17. done
  18.  
  19. PRGDIR=`dirname "$PRG"`
  20. EXECUTABLE=catalina.sh
  21.  
  22. # Check that target executable exists
  23. if $os400; then
  24. # -x will Only work on the os400 if the files are:
  25. # . owned by the user
  26. # . owned by the PRIMARY group of the user
  27. # this will not work if the user belongs in secondary groups
  28. eval
  29. else
  30. if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
  31. echo "Cannot find $PRGDIR/$EXECUTABLE"
  32. echo "The file is absent or does not have execute permission"
  33. echo "This file is needed to run this program"
  34. exit
  35. fi
  36. fi
  37.  
  38. exec "$PRGDIR"/"$EXECUTABLE" start "$@"

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

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

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

代码清单2

  1. elif [ "$1" = "start" ] ; then
  2.  
  3. # 此处省略参数校验的脚本
  4.  
  5. shift
  6. touch "$CATALINA_OUT"
  7. if [ "$1" = "-security" ] ; then
  8. if [ $have_tty -eq ]; then
  9. echo "Using Security Manager"
  10. fi
  11. shift
  12. eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
  13. -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
  14. -Djava.security.manager \
  15. -Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \
  16. -Dcatalina.base="\"$CATALINA_BASE\"" \
  17. -Dcatalina.home="\"$CATALINA_HOME\"" \
  18. -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
  19. org.apache.catalina.startup.Bootstrap "$@" start \
  20. >> "$CATALINA_OUT" >& "&"
  21.  
  22. else
  23. eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
  24. -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
  25. -Dcatalina.base="\"$CATALINA_BASE\"" \
  26. -Dcatalina.home="\"$CATALINA_HOME\"" \
  27. -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
  28. org.apache.catalina.startup.Bootstrap "$@" start \
  29. >> "$CATALINA_OUT" >& "&"
  30.  
  31. fi
  32.  
  33. if [ ! -z "$CATALINA_PID" ]; then
  34. echo $! > "$CATALINA_PID"
  35. fi
  36.  
  37. echo "Tomcat started."

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

代码清单3

  1. /**
  2. * Main method, used for testing only.
  3. *
  4. * @param args Command line arguments to be processed
  5. */
  6. public static void main(String args[]) {
  7.  
  8. if (daemon == null) {
  9. // Don't set daemon until init() has completed
  10. Bootstrap bootstrap = new Bootstrap();
  11. try {
  12. bootstrap.init();
  13. } catch (Throwable t) {
  14. t.printStackTrace();
  15. return;
  16. }
  17. daemon = bootstrap;
  18. }
  19.  
  20. try {
  21. String command = "start";
  22. if (args.length > 0) {
  23. command = args[args.length - 1];
  24. }
  25.  
  26. if (command.equals("startd")) {
  27. args[args.length - 1] = "start";
  28. daemon.load(args);
  29. daemon.start();
  30. } else if (command.equals("stopd")) {
  31. args[args.length - 1] = "stop";
  32. daemon.stop();
  33. } else if (command.equals("start")) {
  34. daemon.setAwait(true);
  35. daemon.load(args);
  36. daemon.start();
  37. } else if (command.equals("stop")) {
  38. daemon.stopServer(args);
  39. } else {
  40. log.warn("Bootstrap: command \"" + command + "\" does not exist.");
  41. }
  42. } catch (Throwable t) {
  43. t.printStackTrace();
  44. }
  45.  
  46. }

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

步骤一 初始化Bootstrap

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

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

代码清单4

  1. /**
  2. * Initialize daemon.
  3. */
  4. public void init()
  5. throws Exception
  6. {
  7.  
  8. // Set Catalina path
  9. setCatalinaHome();
  10. setCatalinaBase();
  11.  
  12. initClassLoaders();
  13.  
  14. Thread.currentThread().setContextClassLoader(catalinaLoader);
  15.  
  16. SecurityClassLoad.securityClassLoad(catalinaLoader);
  17.  
  18. // Load our startup class and call its process() method
  19. if (log.isDebugEnabled())
  20. log.debug("Loading startup class");
  21. Class<?> startupClass =
  22. catalinaLoader.loadClass
  23. ("org.apache.catalina.startup.Catalina");
  24. Object startupInstance = startupClass.newInstance();
  25.  
  26. // Set the shared extensions class loader
  27. if (log.isDebugEnabled())
  28. log.debug("Setting startup class properties");
  29. String methodName = "setParentClassLoader";
  30. Class<?> paramTypes[] = new Class[1];
  31. paramTypes[0] = Class.forName("java.lang.ClassLoader");
  32. Object paramValues[] = new Object[1];
  33. paramValues[0] = sharedLoader;
  34. Method method =
  35. startupInstance.getClass().getMethod(methodName, paramTypes);
  36. method.invoke(startupInstance, paramValues);
  37.  
  38. catalinaDaemon = startupInstance;
  39.  
  40. }

步骤二 加载、解析server.xml配置文件

  当传递参数start的时候,会调用Bootstrap的load方法(见代码清单5),其作用是用反射调用catalinaDaemon(类型是Catalina)的load方法加载和解析server.xml配置文件,具体细节已在《TOMCAT源码分析——SERVER.XML文件的加载与解析》一文中详细介绍,有兴趣的朋友可以选择阅读。

代码清单5

  1. /**
  2. * Load daemon.
  3. */
  4. private void load(String[] arguments)
  5. throws Exception {
  6.  
  7. // Call the load() method
  8. String methodName = "load";
  9. Object param[];
  10. Class<?> paramTypes[];
  11. if (arguments==null || arguments.length==0) {
  12. paramTypes = null;
  13. param = null;
  14. } else {
  15. paramTypes = new Class[1];
  16. paramTypes[0] = arguments.getClass();
  17. param = new Object[1];
  18. param[0] = arguments;
  19. }
  20. Method method =
  21. catalinaDaemon.getClass().getMethod(methodName, paramTypes);
  22. if (log.isDebugEnabled())
  23. log.debug("Calling startup class " + method);
  24. method.invoke(catalinaDaemon, param);
  25.  
  26. }

步骤三 启动Tomcat

  当传递参数start的时候,调用Bootstrap的load方法之后会接着调用start方法(见代码清单6)启动Tomcat,此方法实际是用反射调用了catalinaDaemon(类型是Catalina)的start方法。

代码清单6

  1. /**
  2. * Start the Catalina daemon.
  3. */
  4. public void start()
  5. throws Exception {
  6. if( catalinaDaemon==null ) init();
  7.  
  8. Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
  9. method.invoke(catalinaDaemon, (Object [])null);
  10.  
  11. }

Catalina的start方法(见代码清单7)的执行步骤如下:

  1. 验证Server容器是否已经实例化。如果没有实例化Server容器,还会再次调用Catalina的load方法加载和解析server.xml,这也说明Tomcat只允许Server容器通过配置在server.xml的方式生成,用户也可以自己实现Server接口创建自定义的Server容器以取代默认的StandardServer。
  2. 启动Server容器,有关容器的启动过程的分析可以参考《TOMCAT源码分析——生命周期管理》一文的内容。
  3. 设置关闭钩子。这么说可能有些不好理解,那就换个说法。Tomcat本身可能由于所在机器断点,程序bug甚至内存溢出导致进程退出,但是Tomcat可能需要在退出的时候做一些清理工作,比如:内存清理、对象销毁等。这些清理动作需要封装在一个Thread的实现中,然后将此Thread对象作为参数传递给Runtime的addShutdownHook方法即可。
  4. 最后调用Catalina的await方法循环等待接收Tomcat的shutdown命令。
  5. 如果Tomcat运行正常且没有收到shutdown命令,是不会向下执行stop方法的,当接收到shutdown命令,Catalina的await方法会退出循环等待,然后顺序执行stop方法停止Tomcat。

代码清单7

  1. /**
  2. * Start a new server instance.
  3. */
  4. public void start() {
  5.  
  6. if (getServer() == null) {
  7. load();
  8. }
  9.  
  10. if (getServer() == null) {
  11. log.fatal("Cannot start server. Server instance is not configured.");
  12. return;
  13. }
  14.  
  15. long t1 = System.nanoTime();
  16.  
  17. // Start the new server
  18. try {
  19. getServer().start();
  20. } catch (LifecycleException e) {
  21. log.error("Catalina.start: ", e);
  22. }
  23.  
  24. long t2 = System.nanoTime();
  25. if(log.isInfoEnabled())
  26. log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
  27.  
  28. try {
  29. // Register shutdown hook
  30. if (useShutdownHook) {
  31. if (shutdownHook == null) {
  32. shutdownHook = new CatalinaShutdownHook();
  33. }
  34. Runtime.getRuntime().addShutdownHook(shutdownHook);
  35.  
  36. // If JULI is being used, disable JULI's shutdown hook since
  37. // shutdown hooks run in parallel and log messages may be lost
  38. // if JULI's hook completes before the CatalinaShutdownHook()
  39. LogManager logManager = LogManager.getLogManager();
  40. if (logManager instanceof ClassLoaderLogManager) {
  41. ((ClassLoaderLogManager) logManager).setUseShutdownHook(
  42. false);
  43. }
  44. }
  45. } catch (Throwable t) {
  46. // This will fail on JDK 1.2. Ignoring, as Tomcat can run
  47. // fine without the shutdown hook.
  48. }
  49.  
  50. if (await) {
  51. await();
  52. stop();
  53. }
  54.  
  55. }

Catalina的await方法(见代码清单8)实际只是代理执行了Server容器的await方法。

代码清单8

  1. /**
  2. * Await and shutdown.
  3. */
  4. public void await() {
  5.  
  6. getServer().await();
  7.  
  8. }

以Server的默认实现StandardServer为例,其await方法(见代码清单9)的执行步骤如下:

  1. 创建socket连接的服务端对象ServerSocket;
  2. 循环等待接收客户端发出的命令,如果接收到的命令与SHUTDOWN匹配(由于使用了equals,所以shutdown命令必须是大写的),那么退出循环等待。

代码清单9

  1. public void await() {
  2. // Negative values - don't wait on port - tomcat is embedded or we just don't like ports gja
  3. if( port == -2 ) {
  4. // undocumented yet - for embedding apps that are around, alive.
  5. return;
  6. }
  7. if( port==-1 ) {
  8. while( true ) {
  9. try {
  10. Thread.sleep( 10000 );
  11. } catch( InterruptedException ex ) {
  12. }
  13. if( stopAwait ) return;
  14. }
  15. }
  16.  
  17. // Set up a server socket to wait on
  18. ServerSocket serverSocket = null;
  19. try {
  20. serverSocket =
  21. new ServerSocket(port, 1,
  22. InetAddress.getByName(address));
  23. } catch (IOException e) {
  24. log.error("StandardServer.await: create[" + address
  25. + ":" + port
  26. + "]: ", e);
  27. System.exit(1);
  28. }
  29.  
  30. // Loop waiting for a connection and a valid command
  31. while (true) {
  32.  
  33. // Wait for the next connection
  34. Socket socket = null;
  35. InputStream stream = null;
  36. try {
  37. socket = serverSocket.accept();
  38. socket.setSoTimeout(10 * 1000); // Ten seconds
  39. stream = socket.getInputStream();
  40. } catch (AccessControlException ace) {
  41. log.warn("StandardServer.accept security exception: "
  42. + ace.getMessage(), ace);
  43. continue;
  44. } catch (IOException e) {
  45. log.error("StandardServer.await: accept: ", e);
  46. System.exit(1);
  47. }
  48.  
  49. // Read a set of characters from the socket
  50. StringBuilder command = new StringBuilder();
  51. int expected = 1024; // Cut off to avoid DoS attack
  52. while (expected < shutdown.length()) {
  53. if (random == null)
  54. random = new Random();
  55. expected += (random.nextInt() % 1024);
  56. }
  57. while (expected > 0) {
  58. int ch = -1;
  59. try {
  60. ch = stream.read();
  61. } catch (IOException e) {
  62. log.warn("StandardServer.await: read: ", e);
  63. ch = -1;
  64. }
  65. if (ch < 32) // Control character or EOF terminates loop
  66. break;
  67. command.append((char) ch);
  68. expected--;
  69. }
  70.  
  71. // Close the socket now that we are done with it
  72. try {
  73. socket.close();
  74. } catch (IOException e) {
  75. // Ignore
  76. }
  77.  
  78. // Match against our command string
  79. boolean match = command.toString().equals(shutdown);
  80. if (match) {
  81. log.info(sm.getString("standardServer.shutdownViaPort"));
  82. break;
  83. } else
  84. log.warn("StandardServer.await: Invalid command '" +
  85. command.toString() + "' received");
  86.  
  87. }
  88.  
  89. // Close the server socket and return
  90. try {
  91. serverSocket.close();
  92. } catch (IOException e) {
  93. // Ignore
  94. }
  95.  
  96. }

至此,Tomcat启动完毕。很多人可能会问,执行sh shutdown.sh脚本时,是如何与Tomcat进程通信的呢?如果要与Tomcat的ServerSocket通信,socket客户端如何知道服务端的连接地址与端口呢?下面会慢慢说明。

停止过程分析

我们停止Tomcat的命令如下:

  1. sh shutdown.sh

所以,将从shell脚本shutdown.sh开始分析Tomcat的停止过程。shutdown.sh的脚本代码见代码清单10。

代码清单10

  1. os400=false
  2. case "`uname`" in
  3. OS400*) os400=true;;
  4. esac
  5.  
  6. # resolve links - $0 may be a softlink
  7. PRG="$0"
  8.  
  9. while [ -h "$PRG" ] ; do
  10. ls=`ls -ld "$PRG"`
  11. link=`expr "$ls" : '.*-> \(.*\)$'`
  12. if expr "$link" : '/.*' > /dev/null; then
  13. PRG="$link"
  14. else
  15. PRG=`dirname "$PRG"`/"$link"
  16. fi
  17. done
  18.  
  19. PRGDIR=`dirname "$PRG"`
  20. EXECUTABLE=catalina.sh
  21.  
  22. # Check that target executable exists
  23. if $os400; then
  24. # -x will Only work on the os400 if the files are:
  25. # 1. owned by the user
  26. # 2. owned by the PRIMARY group of the user
  27. # this will not work if the user belongs in secondary groups
  28. eval
  29. else
  30. if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
  31. echo "Cannot find $PRGDIR/$EXECUTABLE"
  32. echo "The file is absent or does not have execute permission"
  33. echo "This file is needed to run this program"
  34. exit 1
  35. fi
  36. fi
  37.  
  38. exec "$PRGDIR"/"$EXECUTABLE" stop "$@"

代码清单10和代码清单1非常相似,其中也有两个主要的变量,分别是:

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

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

代码清单11

  1. elif [ "$1" = "stop" ] ; then
  2.  
  3. #省略参数校验脚本
  4.  
  5. eval "\"$_RUNJAVA\"" $LOGGING_MANAGER $JAVA_OPTS \
  6. -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
  7. -Dcatalina.base="\"$CATALINA_BASE\"" \
  8. -Dcatalina.home="\"$CATALINA_HOME\"" \
  9. -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
  10. org.apache.catalina.startup.Bootstrap "$@" stop
     

从代码清单11可以看出,最终使用java命令执行了org.apache.catalina.startup.Bootstrap类中的main方法,参数是stop。从代码清单3可以看出,当传递参数stop的时候,command等于stop,此时main方法的执行步骤如下:

步骤一 初始化Bootstrap

  已经在启动过程分析中介绍, 不再赘述。

步骤二 停止服务

  通过调用Bootstrap的stopServer方法(见代码清单12)停止Tomcat,其实质是用反射调用catalinaDaemon(类型是Catalina)的stopServer方法。

代码清单12

  1. /**
  2. * Stop the standalone server.
  3. */
  4. public void stopServer(String[] arguments)
  5. throws Exception {
  6.  
  7. Object param[];
  8. Class<?> paramTypes[];
  9. if (arguments==null || arguments.length==0) {
  10. paramTypes = null;
  11. param = null;
  12. } else {
  13. paramTypes = new Class[1];
  14. paramTypes[0] = arguments.getClass();
  15. param = new Object[1];
  16. param[0] = arguments;
  17. }
  18. Method method =
  19. catalinaDaemon.getClass().getMethod("stopServer", paramTypes);
  20. method.invoke(catalinaDaemon, param);
  21.  
  22. }

Catalina的stopServer方法(见代码清单13)的执行步骤如下:

  1. 创建Digester解析server.xml文件(此处只解析<Server>标签),以构造出Server容器(此时Server容器的子容器没有被实例化);
  2. 从实例化的Server容器获取Server的socket监听端口和地址,然后创建Socket对象连接启动Tomcat时创建的ServerSocket,最后向ServerSocket发送SHUTDOWN命令。根据代码清单9的内容,ServerSocket循环等待接收到SHUTDOWN命令后,最终调用stop方法停止Tomcat。

代码清单13

  1. public void stopServer() {
  2. stopServer(null);
  3. }
  4.  
  5. public void stopServer(String[] arguments) {
  6.  
  7. if (arguments != null) {
  8. arguments(arguments);
  9. }
  10.  
  11. if( getServer() == null ) {
  12. // Create and execute our Digester
  13. Digester digester = createStopDigester();
  14. digester.setClassLoader(Thread.currentThread().getContextClassLoader());
  15. File file = configFile();
  16. try {
  17. InputSource is =
  18. new InputSource("file://" + file.getAbsolutePath());
  19. FileInputStream fis = new FileInputStream(file);
  20. is.setByteStream(fis);
  21. digester.push(this);
  22. digester.parse(is);
  23. fis.close();
  24. } catch (Exception e) {
  25. log.error("Catalina.stop: ", e);
  26. System.exit(1);
  27. }
  28. }
  29.  
  30. // Stop the existing server
  31. try {
  32. if (getServer().getPort()>0) {
  33. Socket socket = new Socket(getServer().getAddress(),
  34. getServer().getPort());
  35. OutputStream stream = socket.getOutputStream();
  36. String shutdown = getServer().getShutdown();
  37. for (int i = 0; i < shutdown.length(); i++)
  38. stream.write(shutdown.charAt(i));
  39. stream.flush();
  40. stream.close();
  41. socket.close();
  42. } else {
  43. log.error(sm.getString("catalina.stopServer"));
  44. System.exit(1);
  45. }
  46. } catch (IOException e) {
  47. log.error("Catalina.stop: ", e);
  48. System.exit(1);
  49. }
  50.  
  51. }

最后,我们看看Catalina的stop方法(见代码清单14)的实现,其执行步骤如下:

  1. 将启动过程中添加的关闭钩子移除。Tomcat启动过程辛辛苦苦添加的关闭钩子为什么又要去掉呢?因为关闭钩子是为了在JVM异常退出后,进行资源的回收工作。主动停止Tomcat时调用的stop方法里已经包含了资源回收的内容,所以不再需要这个钩子了。
  2. 停止Server容器。有关容器的停止内容,请阅读《TOMCAT源码分析——生命周期管理》一文。

代码清单14

  1. /**
  2. * Stop an existing server instance.
  3. */
  4. public void stop() {
  5.  
  6. try {
  7. // Remove the ShutdownHook first so that server.stop()
  8. // doesn't get invoked twice
  9. if (useShutdownHook) {
  10. Runtime.getRuntime().removeShutdownHook(shutdownHook);
  11.  
  12. // If JULI is being used, re-enable JULI's shutdown to ensure
  13. // log messages are not lost jiaan
  14. LogManager logManager = LogManager.getLogManager();
  15. if (logManager instanceof ClassLoaderLogManager) {
  16. ((ClassLoaderLogManager) logManager).setUseShutdownHook(
  17. true);
  18. }
  19. }
  20. } catch (Throwable t) {
  21. // This will fail on JDK 1.2. Ignoring, as Tomcat can run
  22. // fine without the shutdown hook.
  23. }
  24.  
  25. // Shut down the server
  26. try {
  27. getServer().stop();
  28. } catch (LifecycleException e) {
  29. log.error("Catalina.stop", e);
  30. }
  31.  
  32. }

总结

  通过对Tomcat源码的分析我们了解到Tomcat的启动和停止都离不开org.apache.catalina.startup.Bootstrap。当停止Tomcat时,已经启动的Tomcat作为socket服务端,停止脚本启动的Bootstrap进程作为socket客户端向服务端发送shutdown命令,两个进程通过共享server.xml里Server标签的端口以及地址信息打通了socket的通信。

如需转载,请标明本文作者及出处——作者:jiaan.gja,本文原创首发:博客园,原文链接:http://www.cnblogs.com/jiaan-geng/p/4872550.html

Tomcat源码分析——启动与停止服务的更多相关文章

  1. TOMCAT源码分析(启动框架)

    建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动 ...

  2. tomcat 源码分析

    Tomcat源码分析——Session管理分析(下)    Tomcat源码分析——Session管理分析(上)     Tomcat源码分析——请求原理分析(下)     Tomcat源码分析——请 ...

  3. [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat

    概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...

  4. Tomcat源码分析之—具体启动流程分析

    从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息 ...

  5. Tomcat源码分析三:Tomcat启动加载过程(一)的源码解析

    Tomcat启动加载过程(一)的源码解析 今天,我将分享用源码的方式讲解Tomcat启动的加载过程,关于Tomcat的架构请参阅<Tomcat源码分析二:先看看Tomcat的整体架构>一文 ...

  6. Tomcat源码分析

    前言: 本文是我阅读了TOMCAT源码后的一些心得. 主要是讲解TOMCAT的系统框架, 以及启动流程.若有错漏之处,敬请批评指教! 建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, ...

  7. Tomcat源码分析--转

    一.架构 下面谈谈我对Tomcat架构的理解 总体架构: 1.面向组件架构 2.基于JMX 3.事件侦听 1)面向组件架构 tomcat代码看似很庞大,但从结构上看却很清晰和简单,它主要由一堆组件组成 ...

  8. Tomcat源码分析——Session管理分析(上)

    前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对 ...

  9. Tomcat源码分析——请求原理分析(中)

    前言 在<TOMCAT源码分析——请求原理分析(上)>一文中已经介绍了关于Tomcat7.0处理请求前作的初始化和准备工作,请读者在阅读本文前确保掌握<TOMCAT源码分析——请求原 ...

随机推荐

  1. django:multivaluedictkeyerror错误

    查了一下,是因为获取前台数据时,用了request.POST[],改用request.POST.get()之后没有这个报错了 细节: request.POST是用来接受从前端表单中传过来的数据,比如用 ...

  2. 查找 SQL SERVER 所有表记录数

    -- 所有表的记录数 SELECT a.name, b.rowsFROM sysobjects AS a INNER JOIN sysindexes AS b ON a.id = b.idWHERE ...

  3. leetcode 实现strStr()

    实现strStr()函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存在,则返回 ...

  4. Let it crash philosophy part II

    Designing fault tolerant systems is extremely difficult.  You can try to anticipate and reason about ...

  5. AndroidSDK下载

    C:\Windows\System32\drivers\etc\hosts74.125.237.1 dl-ssl.google.com

  6. struts2 Convention插件好处及使用

    现在JAVA开发都流行SSH.而很大部分公司也使用了struts2进行开发..因为struts2提供了很多插件和标签方便使用..在之前开发过程中总发现使用了struts2会出现很多相应的配合文件.如果 ...

  7. You can't specify target table 'e' for update in FROM clause

    UPDATE emp e SET e.salary=e.salary+7 WHERE e.id IN(SELECT e1.id FROM emp e1,dept d WHERE e1.dep_id=d ...

  8. codeforces 1096 题解

    A: 发现最优的方案一定是选 $ l $ 和 $ 2 * l $,题目保证有解,直接输出即可 #include <bits/stdc++.h> #define Fast_cin ios:: ...

  9. 老调重弹-access注入过主机卫

    本文作者:i春秋签约作家——非主流 大家好,我是来自农村的非主流,今天就给在座的各位表演个绝活. 首先打开服务器上安装了主机卫士的网站. 尝试在变量id的值后面插入万恶的单引号,根据报错,我们可以分析 ...

  10. 如何在CentOS 7上使用vsftpd(FTP)的配置文件介绍

    vsftpd.conf - vsftpd的配置文件. 描述 vsftpd.conf可用于控制vsftpd行为的各个方面. 默认情况下,vsftpd在/etc/vsftpd.conf位置查找此文件. 但 ...