Tomcat学习2:一键启动以及源码阅读

2021/11/1 12:40:14

本文主要是介绍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() {
        // Set configuration source
 3         ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
 4         File file = configFile();
 5 
 6         // Create and execute our Digester 
           // 1:创建各组件:server,service,ThreadPool,Listener等
 7         Digester digester = createStartDigester();
 8         // 2:解析server.xml
 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 {
               // 3:调用server的init方法初始化Tomcat各组件
32             getServer().init();
33         } catch (LifecycleException e) {
34             
35         }
36       
37     }

核心调用了server的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方法,初始化各组件

 先看第二步,调用了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方法,最终初始化了Host类的init方法(Container默认实现)和start方法

6:Host类

 6.1: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方法,持续进行子组件的初始化工作。



这篇关于Tomcat学习2:一键启动以及源码阅读的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程