Tomcat源码解析-启动过程分析之主干流程

Tomcat启动入口就在脚本startup.sh中,具体脚本可以看tomcat的源码,这个启动脚本主要用来判断环境,
找到catalina.sh脚本路径,将启动参数传递给catalina.sh执行。
catalina.sh start 最终会执行org.apache.catalina.startup.Bootstrap中的main方法,并把start参数传入。
以后分析Tomcat关闭的时候,也是一个套路,最终都会调用到org.apache.catalina.startup.Bootstrap的main方法,并把stop参数传入。

分析main方法:

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

/**

       * 通过提供的脚本启动Tomcat时的主方法和入口点。

       *

       * @param args 要处理的命令行参数

       */

      public static void main(String args[]) {

          //main使用的守护进程对象。

          synchronized (daemonLock) {

              //daemon是volatile修饰的Bootstrap对象

              if (daemon == null) {

                  //在init()完成之前不要设置守护进程

                  Bootstrap bootstrap = new Bootstrap();

                  try {

                      //初始化守护进程。

                      bootstrap.init();

                  } catch (Throwable t) {

                      handleThrowable(t);

                      t.printStackTrace();

                      return;

                  }

                  daemon = bootstrap;

              } else {

                  // W当作为服务运行时,要停止的调用将位于新线程上,

                  // 因此请确保使用了正确的类加载器,以防止出现一系列类未找到异常。直到init()完成

                  Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);

              }

          }

          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")) {

                  //启动操作

                  //通过反射调用守护进程引用的org.apache.catalina.startup.Catalina实例的setAwait方法

                  daemon.setAwait(true);

                  //调用Catalina实例的load方法

                  daemon.load(args);

                  //start方法

                  daemon.start();

                  //反射调用Catalina实例的getServer方法,返回的对象为空时,终止当前运行的Java虚拟机。

                  if (null == daemon.getServer()) {

                      System.exit(1);

                  }

              } else if (command.equals("stop")) {

                  //通过反射调用Catalina的stopServer方法。

                  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.");

              }

          } catch (Throwable t) {

              // Unwrap the Exception for clearer error reporting

              if (t instanceof InvocationTargetException &&

                      t.getCause() != null) {

                  t = t.getCause();

              }

              handleThrowable(t);

              t.printStackTrace();

              System.exit(1);

          }

      }

启动过程有两步操作:
1、初始化守护进程,获取类加载器和反射实例化org.apache.catalina.startup.Catalina。
2、根据启动参数start,通过反射,依次执行Catalina实例的setAwait、load、start方法。

下面主要分析Catalina实例的setAwait、load、start方法:

1 setAwait

入参为true

[Java] 纯文本查看 复制代码

?


1

2

3

4

5

6

7

8

/**

   * Use await.

   */

   protected boolean await = false;

   public void setAwait(boolean b) {

       await = b;

   }

2 load

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

/**

        * 启动一个新的服务器实例。

        */

       public void load() {

           //防止重复加载。

           if (loaded) {

               return;

           }

           loaded = true;

           long t1 = System.nanoTime();

           //创建java.io.tmpdir文件夹

           initDirs();

           // Before digester - it may be needed

           //初始化jmx的环境变量

           initNaming();

           // Create and execute our Digester

           //创建和配置将用于启动的Digester。

           //配置解析server.xml中各个标签的解析类

           Digester digester = createStartDigester();

           InputSource inputSource = null;

           InputStream inputStream = null;

           File file = null;

           try {

               //下面一大段都是为了加载conf/server.xml配置文件,失败就加载server-embed.xml

               ...

               try {

                   inputSource.setByteStream(inputStream);

                   //把Catalina作为一个顶级容器

                   digester.push(this);

                   //解析过程会实例化各个组件,比如Server、Container、Connector等

                   digester.parse(inputSource);

               } catch (SAXParseException spe) {

                   log.warn("Catalina.start using " + getConfigFile() + ": " +

                           spe.getMessage());

                   return;

               } catch (Exception e) {

                   log.warn("Catalina.start using " + getConfigFile() + ": ", e);

                   return;

               }

           } finally {

               if (inputStream != null) {

                   try {

                       inputStream.close();

                   } catch (IOException e) {

                       // Ignore

                   }

               }

           }

           //这里的server在解析xml之后就有值了,这是Server的Catalina信息

           getServer().setCatalina(this);

           getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());

           getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

           // Stream redirection

           initStreams();

           // Start the new server

           try {

               //生命周期init方法

               getServer().init();

           } catch (LifecycleException e) {

               if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {

                   throw new java.lang.Error(e);

               } else {

                   log.error("Catalina.start", e);

               }

           }

           long t2 = System.nanoTime();

           if (log.isInfoEnabled()) {

               log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");

           }

       }

主要流程:

  • 初始化JMX环境变量
  • 创建和配置Digester
  • 读取配置文件conf/server.xml配置文件,失败就加载server-embed.xml
  • 通过Digester解析配置文件,并将当前Catalina作为最顶层容器,解析过程会实例化各种组件
  • 设置Server组件的catalina信息
  • 调用Server组件的生命周期init方法

解析配置文件,注入catalina的各种组件后面分析。
下面看start方法:

3 start

[Java] 纯文本查看 复制代码

?


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

/**

         * 启动一个新的服务器实例。

         */

        public void start() {

            //如果Server组件不存在,则重新执行load方法

            if (getServer() == null) {

                load();

            }

            //依然不存在就返回

            if (getServer() == null) {

                log.fatal("Cannot start server. Server instance is not configured.");

                return;

            }

            long t1 = System.nanoTime();

            // Start the new server

            try {

                //调用Server的start方法

                getServer().start();

            } catch (LifecycleException e) {

                log.fatal(sm.getString("catalina.serverStartFail"), e);

                try {

                    //异常的时候调用Server的destroy方法

                    getServer().destroy();

                } catch (LifecycleException e1) {

                    log.debug("destroy() failed for failed Server ", e1);

                }

                return;

            }

            long t2 = System.nanoTime();

            if (log.isInfoEnabled()) {

                log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");

            }

            // Register shutdown hook

            //注册关闭钩子

            if (useShutdownHook) {

                if (shutdownHook == null) {

                    // Catalina.this.stop();

                    shutdownHook = new CatalinaShutdownHook();

                }

                Runtime.getRuntime().addShutdownHook(shutdownHook);

                // If JULI is being used, disable JULI‘s shutdown hook since

                // shutdown hooks run in parallel and log messages may be lost

                // if JULI‘s hook completes before the CatalinaShutdownHook()

                LogManager logManager = LogManager.getLogManager();

                if (logManager instanceof ClassLoaderLogManager) {

                    ((ClassLoaderLogManager) logManager).setUseShutdownHook(

                            false);

                }

            }

            // Bootstrap中会设置await为true,其目的在于让tomcat在shutdown端口阻塞监听关闭命令

            if (await) {

                //等待收到正确的关机命令,然后返回。

                await();

                //停止现有的服务器实例。

                stop();

            }

        }

流程:

  • 调用Server组件的start方法,开启一个新的服务。
  • 注册关闭钩子
  • 让Tomcat在shutdown端口阻塞监听关闭命令

本篇目的就是了解整个Tomcat启动的主干流程,体现在代码层的就是依次执行Catalina实例的setAwait、load、start方法。
其中的load方法中的解析配置文件与注册组件、执行生命周期方法init;
start方法中的开启服务、注册关闭钩子、阻塞监听关闭指令等详细细节,将在后期慢慢分析。

更多技术资讯可关注:itheimaGZ获取

原文地址:https://www.cnblogs.com/zhuxiaopijingjing/p/12268061.html

时间: 2024-10-05 23:37:12

Tomcat源码解析-启动过程分析之主干流程的相关文章

Tomcat请求处理过程(Tomcat源码解析五)

前面已经分析完了Tomcat的启动和关闭过程,本篇就来接着分析一下Tomcat中请求的处理过程. 在开始本文之前,咋们首先来看看一个Http请求处理的过程,一般情况下是浏览器发送http请求->建立Socket连接->通过Socket读取数据->根据http协议解析数据->调用后台服务完成响应,详细的流程图如上图所示,等读者读完本篇,应该就清楚了上图所表达的意思.Tomcat既是一个HttpServer也是一个Servlet 容器,那么这里必然也涉及到如上过程,首先根据HTTP协议

TOMCAT源码分析(启动框架)

建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动过程. 如果有不明白的地方, 再来查阅本文, 看是否能得到帮助. 我相信这样效果以及学习速度都会好很多! 1. Tomcat的整体框架结构 Tomcat的基本框架, 分为4个层次. Top Level Elements: Server Service Connector HTTP AJP Conta

spring boot 源码解析 启动流程

spring boot 源码解析 启动流程 在面试过程中经常被问到过spring boot的启动流程,今天就翻一下源码整体看一下: 首先,新建一个启动类,可以看到是首先调用的SpringApplication的静态方法run @SpringBootApplication public class SourceReadApplillcation { public static void main(String[] args) { SpringApplication.run(SourceReadAp

《Druid源码解析(1) Guice和Realtime流程》——图较精简,不错

https://zqhxuyuan.github.io/ 最近两年更新少 任何忧伤,都抵不过世界的美丽 2015-12-08 Druid源码解析(1) Guice和Realtime流程 Source druid Druid is a fast column-oriented distributed data store. http://druid.io/ 当启动Druid的服务,会启动一个java进程,比如run_example_server.sh会启动io.druid.cli.Main exa

Tomcat源码分析——启动与停止服务

前言 熟悉Tomcat的工程师们,肯定都知道Tomcat是如何启动与停止的.对于startup.sh.startup.bat.shutdown.sh.shutdown.bat等脚本或者批处理命令,大家一定知道改如何使用它,但是它们究竟是如何实现的,尤其是shutdown.sh脚本(或者shutdown.bat)究竟是如何和Tomcat进程通信的呢?本文将通过对Tomcat7.0的源码阅读,深入剖析这一过程. 由于在生产环境中,Tomcat一般部署在Linux系统下,所以本文将以startup.s

tomcat源码解析

tomcat+springmvc源码解析视频教程 https://study.163.com/course/introduction.htm?courseId=1209399899&trace_c_p_k2=aa9267699c40462ba96533f2f8982e4c 原文地址:https://blog.51cto.com/3921161/2430346

Fresco源码解析 - 初始化过程分析

使用Fresco之前,一定先要进行初始化,一般初始化的工作会在Application.onCreate()完成,当然也可以在使用Drawee之前完成. Fresco本身提供了两种初始化方式,一种是使用使用默认配置初始化,另一种是使用用户自定义配置. 如下代码是Fresco提供的两个初始化方法.第一个只需要提供一个Context参数,第二个还需要提供 ImagePipeline 的配置实例 - ImagePipelineConfig. /** Initializes Fresco with the

Tomcat源码解析(一)下载源码与导入eclipse

自从写web程序以来,web程序是如何在Tomcat中运行的一直困惑着我,不知道底层的运行机制是无法真正理解web的,所以就开始研究Tomcat源码,Tomcat是一个轻量级的java服务器,再结合<How Tomcat works>和网上大牛博客之后,也算知道了内部的运行架构. 首先去官网下载Tomcat源码,我下载的是apache-tomcat-7.0.63-src(在这里下载),因为源码使用ant和maven管理的,所以要用ant或者maven编译为eclipse工程.ant方法编译时有

tomcat的启动过程(Tomcat源码解析(三))

Tomcat组件生命周期管理 在Tomcat总体结构 (Tomcat源代码解析之二)中,我们列出了Tomcat中Server,Service,Connector,Engine,Host,Context的继承关系图,你会发现它们都实现了org.apache.catalina.Lifecycle接口,而org.apache.catalina.util.LifecycleBase采用了模板方法模式来对所有支持生命周期管理的组件的生命周期各个阶段进行了总体管理,每个需要生命周期管理的组件只需要继承这个基