Flame ---- chapter 3

在Flame ---- Chapter 2中的遗留问题尚未得到解决,在读取socket的inputstream中仍然会阻塞。在本chapter中尝试去阅读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操作

你可能感兴趣的:(Flame ---- chapter 3)