1. 程式人生 > >深入理解 Tomcat(六)原始碼剖析Tomcat 啟動過程----生命週期和容器元件

深入理解 Tomcat(六)原始碼剖析Tomcat 啟動過程----生命週期和容器元件

好了,今天我們繼續分析 tomcat 原始碼, 這是第六篇了, 上一篇我們一邊 debug 一邊研究了 tomcat 的類載入體系, 我覺得效果還不錯, 樓主感覺對 tomcat 的類載入體系的理解又加深了一點. 所以, 我們今天還是按照之前的方式來繼續看原始碼, 一邊 debug, 一邊看, 今天我們分析的是tomcat 中2個非常重要的元件——-生命週期和容器. tomcat 龐大的架構, 他是如何管理每個物件的呢? 我們在深入理解 Tomcat (二) 從巨集觀上理解 Tomcat 元件及架構中說過一段:

基於JMX Tomcat會為每個元件進行註冊過程,通過Registry管理起來,而Registry是基於JMX來實現的,因此在看元件的init和start過程實際上就是初始化MBean和觸發MBean的start方法,會大量看到形如: Registry.getRegistry(null, null).invoke(mbeans, “init”, false); Registry.getRegistry(null, null).invoke(mbeans, “start”, false); 這樣的程式碼,這實際上就是通過JMX管理各種元件的行為和生命期。

當時大家可能還不是很理解這句話, 覺得這是在扯淡, 聽不懂. 好吧, 今天我們就用程式碼說話, 看看 JMX 到底怎麼管理 tomcat 的 元件.

1. 什麼是 JMX?

我們之前說過:

JMX 即 Java Management Extensions(JMX 規範), 是用來對 tomcat 進行管理的. tomcat 中的實現是 commons modeler 庫, Catalina 使用這個庫來編寫託管 Bean 的工作. 託管 Bean 就是用來管理 Catalina 中其他物件的 Bean.

簡單來說: 就是一個可以為Java應用程式或系統植入遠端管理功能的框架。

既然是框架, 肯定要有架構圖:

這裡對上圖中三個分層進行介紹:

  • Probe Level:負責資源的檢測(獲取資訊),包含MBeans,通常也叫做Instrumentation Level。MX管理構件(MBean)分為四種形式,分別是標準管理構件(Standard MBean)、動態管理構件(Dynamic MBean)、開放管理構件(Open Mbean)和模型管理構件(Model MBean)。

  • Agent Level:即MBeanServer,是JMX的核心,負責連線Mbeans和應用程式。

  • Remote Management Level:通過connectors和adaptors來遠端操作MBeanServer,常用的控制檯,例如JConsole、VisualVM

    (等會我們就要用這個)等。

2. 我們看看生命週期元件介面是如何設計的:

這是一張 IDEA 生成的簡單的 StandardHost(Host 容器的標準實現) 的 UML類圖, 基本上, tomcat 的容器類都是這樣的繼承結構.

因此我們就可以直接看下面這張圖:

這裡對上圖中涉及的主要類作個簡單介紹:

  1. Lifecycle:定義了容器生命週期、容器狀態轉換及容器狀態遷移事件的監聽器註冊和移除等主要介面;

  2. LifecycleBase:作為Lifecycle介面的抽象實現類,運用抽象模板模式將所有容器的生命週期及狀態轉換銜接起來,此外還提供了生成LifecycleEvent事件的介面;

  3. LifecycleSupport:提供有關LifecycleEvent事件的監聽器註冊、移除,並且使用經典的監聽器模式,實現事件生成後觸達監聽器的實現;

  4. MBeanRegistration:Java JMX框架提供的註冊MBean的介面,引入此介面是為了便於使用JMX提供的管理功能;

  5. LifecycleMBeanBase:Tomcat提供的對MBeanRegistration的抽象實現類,運用抽象模板模式將所有容器統一註冊到JMX;

  6. 此外,ContainerBase、StandardServer、StandardService、WebappLoader、Connector、StandardContext、StandardEngine、StandardHost、StandardWrapper等容器都繼承了LifecycleMBeanBase,因此這些容器都具有了同樣的生命週期並可以通過JMX進行管理。

3. 再看看我們的容器結構

我們之前說, 如果從巨集觀上講容器, 畫畫圖, 講講就好了, 就可以在腦海裡形成一個映象, 今天, 我們要好好的講講容器, 從程式碼層面去理解他們. 這樣一來, 也順便把我們的容器元件也講了, 等於又講了生命週期元件, 還有容器元件. 一舉兩得. 哈哈哈. 好吧, 不扯了, 回來, 我們繼續講容器. 還是先來一張圖吧:

從上圖中我們可以看到: StandardServer、StandardService、Connector、StandardContext這些容器,彼此之間都有父子關係,每個容器都可能包含零個或者多個子容器,這些子容器可能存在不同型別或者相同型別的多個. 所以他們都包含的關係, 如果讓你來設計這些容器的生命週期, 你會用什麼設計模式呢?

4. 容器初始化, 開始 Debug

首先我們啟動 main 方法:


    public static void main(String args[]) {
        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);// bootstrap 和 Catalina 一脈相連, 這裡設定, 方法內部設定 Catalina 例項setAwait方法
                daemon.load(args);// args 為 空,方法內部呼叫 Catalina 的 load 方法.
                daemon.start();// 相同, 反射呼叫 Catalina 的 start 方法 ,至此,啟動結束
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null==daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }
    }

熟悉這個方法或者看過我們上篇文章的同學都知道, 我已經把類載入那部分程式碼去除了, 因為我們今天不研究類載入. 所以 ,我們看邏輯, 首先, 判斷命令是什麼, 我們現在的命令肯定是 start 啊, 所以進入 else if 塊, 呼叫 load 方法 , 進入 load 方法, 可以看到, 該方法實際上就是 Catalina 類的 load 方法, 那麼我們進入 Catalina 類的 load 方法看看(方法很長, 樓主去除了和今天的模組無關的程式碼):

public void load() {
        // Start the new server
        try {
            getServer().init();
        } catch (LifecycleException e) {
            if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                throw new java.lang.Error(e);
            } else {
                log.error("Catalina.start", e);
            }
        }
 }

可以看到, 這裡有一個我們今天感興趣的方法, getServer.init(), 這個方法看名字是啟動 Server 的初始化, 而 Server 是我們上面圖中最外層的容器. 因此, 我們去看看該方法, 也就是LifecycleBase.init() 方法. 該方法是一個模板方法, 只是定義了一個演算法的骨架, 將一些細節演算法延遲到了子類中. 看, 我們又學到了一個設計模式. 我們看看該方法:

 @Override
    public final synchronized void init() throws LifecycleException {
        // 1
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }
        // 2
        setStateInternal(LifecycleState.INITIALIZING, null, false);

        try {
            // 模板方法
            /**
             * 採用模板方法模式來對所有支援生命週期管理的元件的生命週期各個階段進行了總體管理,
             * 每個需要生命週期管理的元件只需要繼承這個基類,
             * 然後覆蓋對應的鉤子方法即可完成相應的宣告週期階段的管理工作
             */
            initInternal();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.initFail",toString()), t);
        }

        // 3
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    }

我們看看該方法, 這應該就是容器啟動的邏輯了, 先前我們定義了那麼多狀態, 現在用上了. 首先判斷該方法的狀態, 如果不是 NEW, 則丟擲異常, 否則則設定狀態為 INITIALIZING, 然後呼叫一個抽象方法 initInternal , 該方法由子類具體實現. 執行完則修改狀態為 INITIALIZED. 這裡應該是使用了狀態模式. 依賴狀態時,同步該方法, 防止併發錯誤. tomcat 可以的.

5. 那麼我們來看看 StandardServer 是如何實現 initInternal 方法的:

    @Override
    protected void initInternal() throws LifecycleException {

        super.initInternal();

        // Register global String cache
        // Note although the cache is global, if there are multiple Servers
        // present in the JVM (may happen when embedding) then the same cache
        // will be registered under multiple names
        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) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            // Walk the class loader hierarchy. Stop at the system class loader.
            // This will add the shared (if present) and common class loaders
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File (url.toURI());
                                if (f.isFile() &&
                                        f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException e) {
                                // Ignore
                            } catch (IOException e) {
                                // Ignore
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }

6. LifecycleMBeanBase.initInternal() 實現

首先呼叫父類的 super.initInternal() 方法,此initInternal方法用於將容器託管到JMX,便於運維管理:

    @Override
    protected void initInternal() throws LifecycleException {

        // If oname is not null then registration has already happened via
        // preRegister().
        if (oname == null) {
            mserver = Registry.getRegistry(null, null).getMBeanServer();
            oname = register(this, getObjectNameKeyProperties());
        }
    }

7. LifecycleMBeanBase.register 方法實現

LifecycleMBeanBase 會呼叫自身的 register 方法, 該方法會將容器註冊到 MBeanServer:

    protected final ObjectName register(Object obj,
            String objectNameKeyProperties) {

        // Construct an object name with the right domain
        StringBuilder name = new StringBuilder(getDomain());
        name.append(':');
        name.append(objectNameKeyProperties);

        ObjectName on = null;

        try {
            on = new ObjectName(name.toString());
            // 核心實現:registerComponent
            Registry.getRegistry(null, null).registerComponent(obj, on, null);
        } catch (MalformedObjectNameException e) {
            log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name),
                    e);
        } catch (Exception e) {
            log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name),
                    e);
        }

        return on;
    }

該方法內部核心方法是 Registry. registerComponent, 在org.apache.catalina.util 包下, 我們看看該方法實現。

8. Registry.registerComponent 方法實現

  public void registerComponent(Object bean, ObjectName oname, String type)
           throws Exception
    {
        if( log.isDebugEnabled() ) {
            log.debug( "Managed= "+ oname);
        }

        if( bean ==null ) {
            log.error("Null component " + oname );
            return;
        }

        try {
            if( type==null ) {
                type=bean.getClass().getName();
            }

            ManagedBean managed = findManagedBean(bean.getClass(), type);

            // The real mbean is created and registered
            DynamicMBean mbean = managed.createMBean(bean);

            if(  getMBeanServer().isRegistered( oname )) {
                if( log.isDebugEnabled()) {
                    log.debug("Unregistering existing component " + oname );
                }
                getMBeanServer().unregisterMBean( oname );
            }

            getMBeanServer().registerMBean( mbean, oname);
        } catch( Exception ex) {
            log.error("Error registering " + oname, ex );
            throw ex;
        }
    }

該方法會為當前容器建立一個 DynamicMBean , 並且註冊到MBeanServer。呼叫 MBeanServer.registerMBean() 方法。而 MBeanServer 在 javax.management, 也就是 rt.jar 中,該包由 java 的 BootStrap 啟動類載入器載入。

註冊進MBeanServer 的 key 是什麼呢? 相信細心的同學會注意到 LifecycleMBeanBase.getObjectNameKeyProperties 和 LifecycleMBeanBase.getDomain 方法 和
LifecycleMBeanBase.getDomainInternal 方法, 這三個方法由具體子類實現,會生成一個專屬於容器的key。格式為:Catalina:type=Server, 這是 Server 容器的 key, debug 可以看出來:

9. JMX 如何管理 元件?

至此, 我們已經知道 Tomcat 是如何將容器註冊到 MBeanServer 中的。 那麼註冊到 MBeanServer 中後是什麼樣子呢?我們看圖:

這是 JDK 自帶的 JvisualVM 工具, 添加了 MBeans 外掛, 就可以遠端操作容器中的 元件了, 可以看到 Service 容器暴漏了很多介面, 用於運維人員管理容器和元件。

10. 回到 StandardServer.initInternal 方法

好了, 我們回到 StandardServer.initInternal 方法, 回到我們夢最開始的地方,super.initInternal 方法就是將容器註冊到 JMX 中。 那下面的邏輯是做什麼的呢? 在執行完父類的 super.initInternal 的方法後, 該方法又註冊個兩個 JMX 。然後尋啟動子容器的 init 方法:

    // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }

而子容器的 init 方法和 Server 的 init 方法的邏輯基本一致,所以不再贅述。

11. 執行完 getServer().init() 方法後做什麼——容器啟動

Bootstrap 的 load 方法呼叫了 Catalina 的 load 方法 ,該方法呼叫了Server 的init方法,執行完初始化過程,當然就是要執行 start 方法了, 那麼如何執行呢?

Bootstrap 呼叫了 Catalina 的 start 方法,該方法也同樣執行了 Server 的 start 方法, 該方法的具體實現也在LifecycleBase 中:

@Override
    public final synchronized void start() throws LifecycleException {

        if (LifecycleState.STARTING_PREP.equals(state) ||
                LifecycleState.STARTING.equals(state) ||
                LifecycleState.STARTED.equals(state)) {

            if (log.isDebugEnabled()) {
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted",
                        toString()), e);
            } else if (log.isInfoEnabled()) {
                log.info(sm.getString("lifecycleBase.alreadyStarted",
                        toString()));
            }

            return;
        }

        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);
        }

        setStateInternal(LifecycleState.STARTING_PREP, null, false);

        try {
            startInternal();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.startFail",toString()), t);
        }

        if (state.equals(LifecycleState.FAILED) ||
                state.equals(LifecycleState.MUST_STOP)) {
            stop();
        } else {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            if (!state.equals(LifecycleState.STARTING)) {
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            }

            setStateInternal(LifecycleState.STARTED, null, false);
        }
    }

12. StandardServer.startInternal 啟動容器方法實現

可以看到該方法對狀態的判斷特別多,我們感興趣的是 try 塊中的 startInternal() 方法, 同樣, 該方法也是個抽象方法,需要子類去具體實現自己的啟動邏輯。我們看看Server 的啟動邏輯:

 @Override
    protected void startInternal() throws LifecycleException {

        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);//將自身狀態更改為LifecycleState.STARTING;
        globalNamingResources.start();
        // Start our defined Services
        synchronized (services) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();// 啟動所有子容器
            }
        }
    }

13. LifecycleSupport.fireLifecycleEvent()方法實現

該方法首先執行自己的fireLifecycleEvent方法, 該方法內部是LifecycleSupport.fireLifecycleEvent()方法, 我們進入該方法看個究竟:

public void fireLifecycleEvent(String type, Object data) {
        // 事件監聽,觀察者模式的另一種方式
        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        LifecycleListener interested[] = listeners;// 監聽器陣列 關注 事件(啟動或者關閉事件)
        // 迴圈通知所有生命週期時間偵聽器????
        for (int i = 0; i < interested.length; i++)
            // 每個監聽器都有自己的邏輯
            interested[i].lifecycleEvent(event);
    }

該方法很簡單, 樓主沒有刪一行程式碼, 首先, 建立一個事件物件, 然通知所有的監聽器發生了該事件.並做響應.那麼 Server 有哪些監聽器呢?

這些監聽器將根據這個事件的型別做出響應.

14. 我們回到 startInternal 方法, 啟動所有容器

事件監聽結束之後, 呼叫 setState(LifecycleState.STARTING); 表明狀態時開始中, 並且迴圈啟動子容器, 這裡的 Server 啟動的是Service 陣列, 迴圈啟動他們的 start 方法. 以此類推. 啟動所有的容器:

    synchronized (services) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();// 啟動所有子容器
            }
        }

現在我們關注的是Server 容器, 因此, Server 會啟動 services 陣列中的所有 Service 元件。該方法就完成了通知所有監聽器傳送了啟動事件,然後使用觀察者模式,啟動所有子容器,然後子容器繼續遞迴啟動。最後修改自己的狀態並告訴監聽器。

15. 總結

其實樓主在啃程式碼最深的感觸就是設計模式, 真的很牛逼,不知道同學們發現了幾個設計模式,樓主在本篇文章中發現了狀態模式, 觀察者模式,模板方法, 事件監聽,代理模式。真的收益良多。不枉樓主每天看程式碼。

還有就是對 Tomcat 生命週期元件的總結。我們再看看我們的類圖:

tomcat 的主要容器都繼承了 LifecycleMBeanBase 抽象類,該類中關於 init 和 start 兩個模板方法。定義了主要演算法骨架,而方法中又都有抽象方法,需要子類自己去實現。而 LifecycleBase 中又定義瞭如何實現事件監聽代理,LifecycleBase 依賴 LifecycleSupport 去完成真正的事件監聽。對了,監聽器是如何新增進 LifecycleSupport 的呢?LifecycleSupport 中含有
addLifecycleListener 方法。該方法也是被LifecycleBase代理的。而每個容器下面的子容器也是使用相同的邏輯完成初始化和啟動。父容器和子容器使用了聚合的方式設計。

可以說, tomcat的容器的生命週期元件設計是非常牛逼的。我們閱讀原始碼不僅能瞭解他的設計原理,也能同大師交流,學會更多。

好了, 今天的深入理解 Tomcat(六)原始碼剖析Tomcat 啟動過程—-生命週期和容器元件就到這裡,謝謝大家的耐心,再這個世界,耐心和堅持是無比珍貴的。尤其是程式設計師。

good luck !!!!

相關推薦

深入理解 Tomcat原始碼剖析Tomcat 啟動過程----生命週期容器元件

好了,今天我們繼續分析 tomcat 原始碼, 這是第六篇了, 上一篇我們一邊 debug 一邊研究了 tomcat 的類載入體系, 我覺得效果還不錯, 樓主感覺對 tomcat 的類載入體系的理解又加深了一點. 所以, 我們今天還是按照之前的方式來繼續看原

深入理解JVM——類加載器原理

區域 (六) HR tcl parse cep 引用關系 throws wid 我們知道我們編寫的java代碼,會經過編譯器編譯成字節碼文件(class文件),再把字節碼文件裝載到JVM中,映射到各個內存區域中,我們的程序就可以在內存中運行了。那麽字節碼文件是怎樣裝載到JV

深入理解ElasticSearch排序與相關性

排序與相關性 預設情況下,返回的結果是按照 相關性 進行排序的——最相關的文件排在最前。 在本章的後面部分,我們會解釋 相關性 意味著什麼以及它是如何計算的, 不過讓我們首先看看 sort 引數以及如何使用它。 1、排序 為了按照相關性來排序,需要將相關性表示為一個數值

深入理解JVM——類檔案結構——code

Code Java程式中方法體中的程式碼經過Javac編譯器處理之後,最終變成位元組碼指令儲存在Code屬性內。 Code屬性出現在方法表的屬性集合之中,但不是所有的方法表都必須有,譬如介面或者抽象類。 Code是Class檔案中最重要的一個屬性,如果把一個Java程式中的資訊分

深入理解JVM——類檔案結構

Java誕生之初就有的口號,Write Once,Run Anywhere Java規範分為Java語言規範和Java虛擬機器規範,Java實現平臺無關性的基礎是虛擬機器和位元組碼儲存格式,Java虛擬機器不與包括Java在內的任何語言繫結,它只與Class檔案這種特定的二進位制檔案格式

深入理解JVM:虛擬機器類載入機制

虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的類載入機制。 在Java中,型別的載入、連線和初始化過程都是程式在執行期間完成的,這種策略雖然會令類載入時稍微增

深入理解JVM——類載入器原理

我們知道我們編寫的java程式碼,會經過編譯器編譯成位元組碼檔案(class檔案),再把位元組碼檔案裝載到JVM中,對映到各個記憶體區域中,我們的程式就可以在記憶體中運行了。那麼位元組碼檔案是怎樣裝載到JVM中的呢?中間經過了哪些步驟?常說的雙親委派模式又是怎麼回事?本文主要搞清楚這些問題。 類

深入理解 Tomcat原始碼環境搭建 How Tomcat works 原始碼

轉載自:https://blog.csdn.net/qq_38182963/article/details/78660767 為了瞭解 tomcat 的原理, 樓主費勁心思, 從圖書館借來了絕版的<>, 下載了該書中的例項原始碼, 由於該書已經

聊聊高並發二十四解析java.util.concurrent各個組件 深入理解AQS

sar 成功 通知 ati help write ng- ads 同步 近期總體過了下AQS的結構。也在網上看了一些講AQS的文章,大部分的文章都是泛泛而談。又一次看了下AQS的代碼,把一些新的要點拿出來說一說。 AQS是一個管程。提供了一個主要的同步器的

深入理解Plasma2:Plasma 細節剖析

這一系列文章將圍繞以太坊的二層擴容框架,介紹其基本執行原理,具體操作細節,安全性討論以及未來研究方向等。本篇文章主要對 Plasma 一些關鍵操作的細節進行剖析。 在上一篇文章中我們已經理解了什麼是 Plasma 框架以及它是如何執行的,這一篇文章將對其執行過程中的一

聊聊高併發二十四解析java.util.concurrent各個元件 深入理解AQS

最近整體過了下AQS的結構,也在網上看了一些講AQS的文章,大部分的文章都是泛泛而談。重新看了下AQS的程式碼,把一些新的要點拿出來說一說。 AQS是一個管程,提供了一個基本的同步器的能力,包含了一個狀態,修改狀態的原子操作,以及同步執行緒的一系列操作。它是CLHLock

深入剖析Kubernetes學習筆記:深入理解獎項08

fix ble 允許 容器 hello 工作目錄 rwx user open 一、Python 應用案例環境 [root@k8s-node1 Flask]# pwd /opt/Dockerfile/Flask [root@k8s-node1 Flask]# ll tota

SpringBoot 原始碼解析 ----- Spring Boot的核心能力 - 內建Servlet容器原始碼分析Tomcat

Spring Boot預設使用Tomcat作為嵌入式的Servlet容器,只要引入了spring-boot-start-web依賴,則預設是用Tomcat作為Servlet容器: <dependency> <groupId>org.springframework.boot<

深入理解JavaScript

odi 可能 方式 == tin mini 單詞 包括 fun 編寫高質量 JavaScript 代碼的基本要點 轉載:http://wiki.jikexueyuan.com/project/javascript-depth-understanding/start-jav

深入理解 JavaScript

div 參數 面向 描述 pla 耦合 理論 spl 什麽 前言 Bob 大叔提出並發揚了 S.O.L.I.D 五大原則,用來更好地進行面向對象編程,五大原則分別是: The Single Responsibility Principle(單一職責 SRP) The Op

Redis深入學習筆記Redis內存分配

swa 如果 利用 技術分享 拷貝 back width byte hat Redis的高效可以說是輕量級的epoll模型和基於內存的讀寫共同組成的,所以內存的使用就至關重要,本篇主要介紹Redis的內存分配原理。 獲取內存信息命令:info memory used_me

深入理解http------http的緩存機制及原理

dad hl7 工作 tps sla vhk b+ vpp lrn 一、概念基礎 參考原文:https://blog.csdn.net/hiredme/article/details/73468040 http的緩存,主要存在於本地瀏覽器和web代理服務器中。 在

換個角度深入理解GlusterFS

nbsp 負載均衡 高可用性 哈希算法 iops 當前 最新 文件目錄 moosefs GlusterFS(GNU ClusterFile System)是一個開源的分布式文件系統,它的歷史可以追溯到2006年,最初的目標是代替Lustre和GPFS分布式文件系統。經過八年

深入理解計算機CSAPP資源匯總

com 深入理解 cti www. tle computer lan assign ems   用於資源記錄。 視頻: 卡內基梅隆大學 Introduction to Computer Systems CMU 15-213 Fall 作業(labs): Lab Assign

深入理解Plasma3:Plasma MVP

這一系列文章將圍繞以太坊的二層擴容框架,介紹其基本執行原理,具體操作細節,安全性討論以及未來研究方向等。本篇文章主要介紹 Plasma 的一個最小實現 Plasma MVP(Minima Viable Plasma)。 在上一篇文章中我們已經理解了 Plasma 中的