1. 程式人生 > 其它 >Tomcat學習2:一鍵啟動以及原始碼閱讀

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,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方法,持續進行子元件的初始化工作。