Flame ---- chapter 3

在Flame ---- Chapter 2中的遗留问题尚未得到解决,在读取socket的inputstream中仍然会阻塞。在本chapter中尝试去阅读tomcat的启动代码, 了解tomcat如何处理这个问题的

tomcat的整体框架图如下图所示

Tomcat框架图
Tomcat框架图

Tomcat启动流程

        if (daemon == null) {
            daemon = new Bootstrap();
            try {
                daemon.init();
            } catch (Throwable t) {
                t.printStackTrace();
                return;
            }
        }

        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();
        }
  1. 实例化Bootstrap对象daemon, 并init
    1.1 指定init方法的时候主要执行如下流程
    • 初始化CatalinaHome, CatalinaBase等环境参数
    • 初始化common.loader, server.loader, shared.loader等classloader对象,由createClassLoader完成
      • 从catalina.properties(或者由catalina.config环境变量指定)文件中加载common.loader指定的类加载路径, 然后完成其中占位符的替换, 得到这些路径(repository)以及repository_type(is_jar, is_dir, is_glob等参数)
      • 调用ClassLoaderFactory.createClassLoader完成classloader的构造, 最终会构造一个StandardClassLoader对象
      • 这三个classLoader对象的关系是commonLoader下属为catalinaLoader和sharedLoader两个对象, 而最终commonLoader的parent为SystemClassLoader对象
    • 创建Catalina对象,利用反射完成,称为startUpClass
    • 调用Catalina对象的setParentClassLoader方法, 把sharedLoader作为参数传入
    • 最后将BootStrap对象的catalinaDaemon设置为startUpInstance对象
  2. start
    2.1 daemon.setAwait, 最终执行的是Catalina对象的setAwait方法
    2.2 daemon.load, 最终执行的是Catalina对象的load方法
    • 初始化相关的文件夹,包括catalina.home, catalina.base, java.io.tmpdir等参数
    • 初始化Digest对象及其前后置操作
      • Digest是什么:Digester对象可以用于解析XML文档。对于XML文档中定义的每个元素,Digester对象都会检查它是不是要做预先定义的事件。在调用Digester对象的parse方法之前,程序员需要定义好Digetser对象执行哪些动作。【《How Tomcat Works》】
      • 创建startDigester,并创建一些digester规则,用于解析server.xml文档。这些规则包括处理元素标签上定义的属性(properties),设置子元素(包括setServer, setService等函数),以及一些规则集合(EngineRuleSet, ContextRuleSet, NamingRuleSet, ClusterRuleSet等)
    • 加载conf/server.xml文件
      • 前置一些判断,包括判断tomcat文件路径, 以及是否为内嵌的tomcat
      • digest对象执行parse函数,完成server.xml的解析, 实际上委托的Parser是SaxParserImpl对象。
      • digester对象内部实现逻辑:使用SAX解析方式解析完一个标签,然后找到满足这个pattern的所有rule, 并执行这个rule, 其中值得注意的一个逻辑:digester内部有一个stack,然后使用这个stack存储所有ObjectCreateRule的对象。
      • Digest对象首先将当前的Catalina对象放置到stack中,代码如下
        digester.push(this); // 把Catalina对象放置到stack中 digester.parse(inputSource);
      • Digester对象的SetNextRule实现规则
        Object child = digester.peek(0); // 堆栈中从上到下第0个元素
    Object parent = digester.peek(1); // 堆栈中从上到下第1个元素
    if (digester.log.isDebugEnabled()) {
        if (parent == null) {
            digester.log.debug("[SetNextRule]{" + digester.match +
                    "} Call [NULL PARENT]." +
                    methodName + "(" + child + ")");
        } else {
            digester.log.debug("[SetNextRule]{" + digester.match +
                    "} Call " + parent.getClass().getName() + "." +
                    methodName + "(" + child + ")");
        }
    }
    // Call the specified method
    IntrospectionUtils.callMethod1(parent, methodName,
            child, paramType, digester.getClassLoader());

可以发现是从堆栈从上取得前两个元素,然后调用第1个元素,关联第0个元素

2.3 daemon.start, 委托给catalina的start方法

  • ((Lifecycle) getServer()).start()(注意server是在Digester中注册得到的),进入StandardServer.start()
    • 先判断server的状态,防止多次start造成错误
    • lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
      • LifeCycle对象在创建StandardServer对象时创建,Digester对象定义的addNext规则把所有listener都添加到lifecycle中。
      • 触发6个listener的BEFORE_START_EVENT事件,关于listener等组件的详细说明见《Flame ---- Chapter 5》
    • lifecycle.fireLifecycleEvent(START_EVENT, null);
  • addShutdownHook(shutdownHook)
  • 监听某个端口,允许执行shutdown操作
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,083评论 19 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,989评论 6 342
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,391评论 11 349
  • 转自陈明乾的博客,可能有一定更新。 转原文声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、...
    C86guli阅读 2,728评论 1 37
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,803评论 18 399