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

2024-05-13 11:48

本文主要是介绍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获取

这篇关于Tomcat源码解析-启动过程分析之主干流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/985619

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided