zoukankan      html  css  js  c++  java
  • Tomcat学习2:一键启动以及源码阅读

    一次请求在Tomcat中经过的组件

    Tomcat处理一个HTTP请求,在各组件中的流转过程如下图红色箭头:

     一个系统通过如此多的组件组装起来完成一次完成的服务,那如何管理组件的创建、初始化和调用关系?

    Lifecycle

    系统设计要找到不变的点和变化的点,这里不变的地方就是每个组件都要创建、初始化、启动、销毁等,这些状态和状态之间的转化是不变的。变化的是每个组件初始化方法不一样。

    Tomcat把不变的地方抽象出一个生命周期Lifecycle接口,定义了一些不变的方法:init,start,stop,destory,各组件去实现具体的逻辑。在父组件的init方法里面,会调用子组件的Init方法,只要调用最顶层的server组件的Init和start方法,整个tomcat组件都会被启动起来。(组合模式-Lifecycle接口

    生命周期会对应到一个个状态LiftcycleState,状态可作为事件,是可以被监听的。一个组件的状态变化会触发子组件的变化,比如Host容器的启动事件会触发Web应用的扫描和加载(反射)(观察者模式),最终会在Host容器中创建出Context容器,Lifecycle接口里有两个方法:添加监听器和删除监听器。

    LifecycleBase抽象类实现了Lifecycle接口,并把一些公共的逻辑放到基类中,如生命状态的转变和、生命周期事件的触发等,子类就负责自己的初始化、启动和停止等方法(模板模式), 子类的实现会加上Internal后缀,比如InitInternal,startInternal等。

     如何启动Tomcat

    1.Tomcat本质上是一个Java程序,因此startup.sh脚本会启动一个JVM来运行Tomcat的启动类Bootstrap。

    2.Bootstrap的主要任务是初始化Tomcat的类加载器,并且创建Catalina。Tomcat为什么需要自己的类加载器?

    3.Catalina是一个启动类,它通过解析server.xml、创建相应的组件,并调用Server的start方法和init方法。

       Catalina作为管理者,还通过”钩子“处理各种异常,如tomcat关闭时,如何释放资源以及内存数据刷到磁盘等

    4.Server组件的职责就是管理Service组件,它会负责调用Service的start方法。

    5.Service组件的职责就是管理连接器和顶层容器Engine,因此它会调用连接器和Engine的start方法。

    1:Bootstrap类

    Tomcat是通过startup.sh调用了Bootstra的main方法启动

    1.1:main方法:

     1 public static void main(String args[]) {
     2 
     3         synchronized (daemonLock) {
     4             if (daemon == null) {
     5                 // Don't set daemon until init() has completed
     6                 Bootstrap bootstrap = new Bootstrap();
     7                 try {
                           // 1:初始化
     8                     bootstrap.init();
     9                 } catch (Throwable t) {  
    13                 }
    14                 daemon = bootstrap;
    15             } 
    21         }
    22 
    23         try {
    24             String command = "start";
    25             if (args.length > 0) {
    26                 command = args[args.length - 1];
    27             }
    28             // 2:对同的命令做不同的动作
    29             if (command.equals("startd")) {
    30                 args[args.length - 1] = "start";
    31                 daemon.load(args);
    32                 daemon.start();
    33             } else if (command.equals("stopd")) {
    34                 args[args.length - 1] = "stop";
    35                 daemon.stop();
    36             } 
    63         }
    64     } 

    主要做一些初始化init和完成一些动作指令load,start

    1.2:init方法:

     1 public void init() throws Exception {
     2         //1:类加载器
     3         initClassLoaders();
     4 
     5         Thread.currentThread().setContextClassLoader(catalinaLoader);
     6 
     7         SecurityClassLoad.securityClassLoad(catalinaLoader);
     8 
     9         // Load our startup class and call its process() method
    10         if (log.isDebugEnabled())
    11             log.debug("Loading startup class");
    12         Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    13         Object startupInstance = startupClass.getConstructor().newInstance();
    14 
    15         // Set the shared extensions class loader
    16         if (log.isDebugEnabled())
    17             log.debug("Setting startup class properties");
    18         String methodName = "setParentClassLoader";
    19         Class<?> paramTypes[] = new Class[1];
    20         paramTypes[0] = Class.forName("java.lang.ClassLoader");
    21         Object paramValues[] = new Object[1];
    22         paramValues[0] = sharedLoader;
    //2:实例化catalina
    23 Method method = 24 startupInstance.getClass().getMethod(methodName, paramTypes); 25 method.invoke(startupInstance, paramValues); 26 27 catalinaDaemon = startupInstance; 28 }

    1:初始化类加载器,包括了common类加载器,shared类加载器,catalina类加载器(Tomcat类加载器和Jvm类加载器?),其中common类加载器作为父类加载器

    2:实例化Catalina对象,并传入catalinaClassLoader作为parentClassLoader加载子组件,实现catalinaClassLoader和shareClassLoader隔离

    1.3:load方法:

    1 private void load(String[] arguments) throws Exception {
    2 
    3         // Call the load() method
    4         String methodName = "load";
    5         Method method =
    6             catalinaDaemon.getClass().getMethod(methodName, paramTypes);
    7         method.invoke(catalinaDaemon, param);
    8     }

    通过反射调用Catalina类的load方法

    1.4:start方法:

    start方法也通过反射调用了Catalina类的start方法

    2:Catalina

    Catalina作为启动类,它通过解析server.xml,创建相应的组件,并调用Server的start方法和init方法完成Tomcat的启动过程

    2.1:load方法:

     1 public void load() {
    2 // Set configuration source 3 ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile())); 4 File file = configFile(); 5 6 // Create and execute our Digester
    // 1:解析server.xml,创建各组件:server,service,ThreadPool,Listener等 7 Digester digester = createStartDigester(); 8 9 try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) { 10 InputStream inputStream = resource.getInputStream(); 11 InputSource inputSource = new InputSource(resource.getURI().toURL().toString()); 12 inputSource.setByteStream(inputStream); 13 digester.push(this); 14 digester.parse(inputSource); 15 } catch (Exception e) { 16 log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e); 17 if (file.exists() && !file.canRead()) { 18 log.warn(sm.getString("catalina.incorrectPermissions")); 19 } 20 return; 21 } 22 23 getServer().setCatalina(this); 24 getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); 25 getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); 26 27 // Stream redirection 28 initStreams(); 29 30 // Start the new server 31 try {
    // 2:调用server的init方法初始化Tomcat各组件
    32 getServer().init(); 33 } catch (LifecycleException e) { 34 35 } 36 37 }

    1:对server.xml进行解析:

    把Server、Service、Connector、Engine、Host各组件实例化,并维护父子级关系。其中createStartDigester方法如下:

      1 protected Digester createStartDigester() {
      2         long t1=System.currentTimeMillis();
      3         // Initialize the digester
      4         Digester digester = new Digester();
      5         digester.setValidating(false);
      6         digester.setRulesValidation(true);
      7         Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
      8         // Ignore className on all elements
      9         List<String> objectAttrs = new ArrayList<>();
     10         objectAttrs.add("className");
     11         fakeAttributes.put(Object.class, objectAttrs);
     12         // Ignore attribute added by Eclipse for its internal tracking
     13         List<String> contextAttrs = new ArrayList<>();
     14         contextAttrs.add("source");
     15         fakeAttributes.put(StandardContext.class, contextAttrs);
     16         // Ignore Connector attribute used internally but set on Server
     17         List<String> connectorAttrs = new ArrayList<>();
     18         connectorAttrs.add("portOffset");
     19         fakeAttributes.put(Connector.class, connectorAttrs);
     20         digester.setFakeAttributes(fakeAttributes);
     21         digester.setUseContextClassLoader(true);
     22 
     23         // Configure the actions we will be using
     24         digester.addObjectCreate("Server",
     25                                  "org.apache.catalina.core.StandardServer",
     26                                  "className");
     27         digester.addSetProperties("Server");
     28         digester.addSetNext("Server",
     29                             "setServer",
     30                             "org.apache.catalina.Server");
     31 
     32         digester.addObjectCreate("Server/GlobalNamingResources",
     33                                  "org.apache.catalina.deploy.NamingResourcesImpl");
     34         digester.addSetProperties("Server/GlobalNamingResources");
     35         digester.addSetNext("Server/GlobalNamingResources",
     36                             "setGlobalNamingResources",
     37                             "org.apache.catalina.deploy.NamingResourcesImpl");
     38 
     39         digester.addRule("Server/Listener",
     40                 new ListenerCreateRule(null, "className"));
     41         digester.addSetProperties("Server/Listener");
     42         digester.addSetNext("Server/Listener",
     43                             "addLifecycleListener",
     44                             "org.apache.catalina.LifecycleListener");
     45 
     46         digester.addObjectCreate("Server/Service",
     47                                  "org.apache.catalina.core.StandardService",
     48                                  "className");
     49         digester.addSetProperties("Server/Service");
     50         digester.addSetNext("Server/Service",
     51                             "addService",
     52                             "org.apache.catalina.Service");
     53 
     54         digester.addObjectCreate("Server/Service/Listener",
     55                                  null, // MUST be specified in the element
     56                                  "className");
     57         digester.addSetProperties("Server/Service/Listener");
     58         digester.addSetNext("Server/Service/Listener",
     59                             "addLifecycleListener",
     60                             "org.apache.catalina.LifecycleListener");
     61 
     62         //Executor
     63         digester.addObjectCreate("Server/Service/Executor",
     64                          "org.apache.catalina.core.StandardThreadExecutor",
     65                          "className");
     66         digester.addSetProperties("Server/Service/Executor");
     67 
     68         digester.addSetNext("Server/Service/Executor",
     69                             "addExecutor",
     70                             "org.apache.catalina.Executor");
     71 
     72 
     73         digester.addRule("Server/Service/Connector",
     74                          new ConnectorCreateRule());
     75         digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
     76                 new String[]{"executor", "sslImplementationName", "protocol"}));
     77         digester.addSetNext("Server/Service/Connector",
     78                             "addConnector",
     79                             "org.apache.catalina.connector.Connector");
     80 
     81         digester.addRule("Server/Service/Connector", new AddPortOffsetRule());
     82 
     83         digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
     84                                  "org.apache.tomcat.util.net.SSLHostConfig");
     85         digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
     86         digester.addSetNext("Server/Service/Connector/SSLHostConfig",
     87                 "addSslHostConfig",
     88                 "org.apache.tomcat.util.net.SSLHostConfig");
     89 
     90         digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
     91                          new CertificateCreateRule());
     92         digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
     93                          new SetAllPropertiesRule(new String[]{"type"}));
     94         digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
     95                             "addCertificate",
     96                             "org.apache.tomcat.util.net.SSLHostConfigCertificate");
     97 
     98         digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
     99                                  "org.apache.tomcat.util.net.openssl.OpenSSLConf");
    100         digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
    101         digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
    102                             "setOpenSslConf",
    103                             "org.apache.tomcat.util.net.openssl.OpenSSLConf");
    104 
    105         digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
    106                                  "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
    107         digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
    108         digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
    109                             "addCmd",
    110                             "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
    111 
    112         digester.addObjectCreate("Server/Service/Connector/Listener",
    113                                  null, // MUST be specified in the element
    114                                  "className");
    115         digester.addSetProperties("Server/Service/Connector/Listener");
    116         digester.addSetNext("Server/Service/Connector/Listener",
    117                             "addLifecycleListener",
    118                             "org.apache.catalina.LifecycleListener");
    119 
    120         digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
    121                                   null, // MUST be specified in the element
    122                                   "className");
    123         digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
    124         digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
    125                             "addUpgradeProtocol",
    126                             "org.apache.coyote.UpgradeProtocol");
    127 
    128         // Add RuleSets for nested elements
    129         digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
    130         digester.addRuleSet(new EngineRuleSet("Server/Service/"));
    131         digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
    132         digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
    133         addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
    134         digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
    135 
    136         // When the 'engine' is found, set the parentClassLoader.
    137         digester.addRule("Server/Service/Engine",
    138                          new SetParentClassLoaderRule(parentClassLoader));
    139         addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
    140 
    141         long t2=System.currentTimeMillis();
    142         if (log.isDebugEnabled()) {
    143             log.debug("Digester for server.xml created " + ( t2-t1 ));
    144         }
    145         return digester;
    146 
    147     }

    创建StanderService对象过程如下:调用StandardService实例的set方法设置属性,最后调用父节点Server的addService方法,将service添加到Server。

    其他组件创建过程类似(但是Content组件和子组件有点特殊,后面再单独看)

    digester.addObjectCreate("Server/Service",
                                     "org.apache.catalina.core.StandardService",
                                     "className");
            digester.addSetProperties("Server/Service");
            digester.addSetNext("Server/Service",
                                "addService",
                                "org.apache.catalina.Service");

    2:调用standardServer的init方法完成完成Server组件的初始化

    2.2:start方法:

    getServer().start();

    调用了server的start方法启动Tomcat的所有组件

    3:Server类

    server组件的init和start方法,最终调用的是Lifecycle的init和start方法。Lifecycle的实现类LifecycleBase的init方法(模板模式

    3.1:LifecycleBase的init方法:

     1 public final synchronized void init() throws LifecycleException {
     2         if (!state.equals(LifecycleState.NEW)) {
     3             invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
     4         }
     5 
     6         try {
    //1:状态变更事件
    7 setStateInternal(LifecycleState.INITIALIZING, null, false);
    // 2:server的初始化方法
    8 initInternal(); 9 setStateInternal(LifecycleState.INITIALIZED, null, false); 10 } catch (Throwable t) { 11 handleSubClassException(t, "lifecycleBase.initFail", toString()); 12 } 13 }

    1:状态变更事件(观察者模式)

    2:调用StandardServer的initInternal方法,初始化各组件:后面子容器实现类都是Standard- 开头;状态变更事件稍后在看(如何注册事件,怎么通知?)

    整个初始化链路如下图:

    3.2:StandardServer的initInternal方法:

    protected void initInternal() throws LifecycleException {
    
            super.initInternal();
    
            // Initialize utility executor
            reconfigureUtilityExecutor(getUtilityThreadsInternal(utilityThreads));
            register(utilityExecutor, "type=UtilityExecutor");
    
            onameStringCache = register(new StringCache(), "type=StringCache");
    
            // Register the MBeanFactory
            MBeanFactory factory = new MBeanFactory();
            factory.setContainer(this);
            onameMBeanFactory = register(factory, "type=MBeanFactory");
    
            // Register the naming resources
            globalNamingResources.init();
    
            // Populate the extension validator with JARs from common and shared
            // class loaders
            if (getCatalina() != null) {
                ....
            }
            // Initialize our defined Services
            for (int i = 0; i < services.length; i++) {
                services[i].init();
            }
        }

    server的初始化工作,除了对自身做一些基础的初始化,主要是对service组件进行初始化(1个server可对应多个service)

    3.3:StandardServer的startInternal方法:

     

     startInternal方法主要是调用service的startInternal方法,子组件会做一些特殊的动作

    4:Service类

    4.1:initInternal方法:

    protected void initInternal() throws LifecycleException {
    
            super.initInternal();
    
         //1:engine初始化
    if (engine != null) { engine.init(); } // 2:线程池初始化
    for (Executor executor : findExecutors()) { if (executor instanceof JmxEnabled) { ((JmxEnabled) executor).setDomain(getDomain()); } executor.init(); } // 3:Initialize mapper listener mapperListener.init(); // 4:Initialize our defined Connectors synchronized (connectorsLock) { for (Connector connector : connectors) { connector.init(); } } }

    service的初始化比较热闹,主要完成四件事,刚好对应到tomcat架构设计上service的功能

    1)子组件的初始化

    2)公共线程池

    3)mapper listener请求映射

    4)连接器

     4.2:initInternal方法:

    start方法和init方法也比较类似。

    至此,顶层的公共逻辑已经完成,下面分为连接器、处理器、映射Mapper以及公共线程池独立的初始化和启动流程。

    从上面的service的初始化开始进容器组件的初始化最顶层的Engine(Container作为容器组件的公共接口提供服务)。

    5:Engine类

    5.1:initInternal方法

    protected void initInternal() throws LifecycleException {
            getRealm();
            super.initInternal();
        }

    从代码层面看,init方法调用了ContainerBase的init方法,启动一个线程池,那子容器的初始化在哪完成的呢?,继续看

    5.2:startInternal方法

    protected synchronized void startInternal() throws LifecycleException {
    
            // Standard container startup
            super.startInternal();
        }

    调用了ContainerBase的startInternal方法

    protected synchronized void startInternal() throws LifecycleException {
    
            // 1:Start our child containers, if any
            Container children[] = findChildren();
            List<Future<Void>> results = new ArrayList<>();
            for (int i = 0; i < children.length; i++) {
                results.add(startStopExecutor.submit(new StartChild(children[i])));
            }
    
            MultiThrowable multiThrowable = null;
    
            for (Future<Void> result : results) {
                try {
                    result.get();
                } catch (Throwable e) {
                    log.error(sm.getString("containerBase.threadedStartFailed"), e);
                    if (multiThrowable == null) {
                        multiThrowable = new MultiThrowable();
                    }
                    multiThrowable.add(e);
                }
    
            }
            if (multiThrowable != null) {
                throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
                        multiThrowable.getThrowable());
            }
    
            // Start the Valves in our pipeline (including the basic), if any
            if (pipeline instanceof Lifecycle) {
                ((Lifecycle) pipeline).start();
            }
    
            setState(LifecycleState.STARTING);
    
            // Start our thread
            if (backgroundProcessorDelay > 0) {
                monitorFuture = Container.getService(ContainerBase.this).getServer()
                        .getUtilityExecutor().scheduleWithFixedDelay(
                                new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
            }
        }

    上面代码核心在找到子组件并用线程池进行初始化(这里使用了init方法初始化的线程池)。

    private static class StartChild implements Callable<Void> {
    
            private Container child;
    
            public StartChild(Container child) {
                this.child = child;
            }
    
            @Override
            public Void call() throws LifecycleException {
                child.start();
                return null;
            }

    上面线程的start方法调用了LifecycleBase的start方法:

    5.3:LifecycleBase的start方法

    public final synchronized void start() throws LifecycleException {
            //1:状态事件是NEW,执行初始化
            if (state.equals(LifecycleState.NEW)) {
                init();
            } else if (state.equals(LifecycleState.FAILED)) {
                stop();
            } else if (!state.equals(LifecycleState.INITIALIZED) &&
                    !state.equals(LifecycleState.STOPPED)) {
                invalidTransition(Lifecycle.BEFORE_START_EVENT);
            }
    
            try {
                setStateInternal(LifecycleState.STARTING_PREP, null, false);
                //2:执行start方法
                startInternal();
            }
        }

    该方法完成有很多状态监听机制,其中就有对NEW状态执行init的方法,就是我们前面说的初始化Host组件,并调用了start方法进行启动过程

    6:Host类

     6.1:initInternal方法

    用的还是父类ContainerBase的方法,也没有初始化Context容器,整个初始化的启动过程和Enginer类似

    @Override
        protected void initInternal() throws LifecycleException {
            reconfigureStartStopExecutor(getStartStopThreads());
            super.initInternal();
        }

     6.2:startInternal方法

    protected synchronized void startInternal() throws LifecycleException {
    
            // Set error report valve
            String errorValve = getErrorReportValveClass();
            if ((errorValve != null) && (!errorValve.equals(""))) {
                try {
                    boolean found = false;
                    Valve[] valves = getPipeline().getValves();
                    for (Valve valve : valves) {
                        if (errorValve.equals(valve.getClass().getName())) {
                            found = true;
                            break;
                        }
                    }
                    if(!found) {
                        Valve valve =
                            (Valve) Class.forName(errorValve).getConstructor().newInstance();
                        getPipeline().addValve(valve);
                    }
                } catch (Throwable t) {
                   
            }
            super.startInternal();
        }

    对Host的Pipeline添加了value,并调用了父类ContainerBase的startInternal方法(5.3),持续进行子组件的初始化工作。但是前面的server.xml的解析里面可以没有Context,那Context容器又如何初始化呢?

    7:web.xml

  • 相关阅读:
    修改Matlab 2012b默认工作路径
    win7,M​i​n​d​m​a​n​a​g​e​r​2​0​1​2使用模板时弹出Runtime error R6025解决方法
    Win7 Qt4.8.5+QtCreator2.8.0+mingw配置过程
    RabbitMQ介绍和延迟队列
    FastDFS介绍
    SpringMVC的工作流程
    spring mvc入参有Date类型
    spring 定时任务的 时间配置cron表达式
    Transactional介绍及使用
    spring cloud 集成rabbitMQ实现延时队列
  • 原文地址:https://www.cnblogs.com/at20191018/p/15460388.html
Copyright © 2011-2022 走看看