標籤: 網頁設計

  • ASP.NET Core Blazor Webassembly 之 漸進式應用(PWA)

    ASP.NET Core Blazor Webassembly 之 漸進式應用(PWA)

    Blazor支持漸進式應用開發也就是PWA。使用PWA模式可以使得web應用有原生應用般的體驗。

    什麼是PWA

    PWA應用是指那些使用指定技術和標準模式來開發的web應用,這將同時賦予它們web應用和原生應用的特性。
    例如,web應用更加易於發現——相比於安裝應用,訪問一個網站顯然更加容易和迅速,並且你可以通過一個鏈接來分享web應用。
    在另一方面,原生應用與操作系統可以更加完美的整合,也因此為用戶提供了無縫的用戶體驗。你可以通過安裝應用使得它在離線的狀態下也可以運行,並且相較於使用瀏覽器訪問,用戶也更喜歡通過點擊主頁上的圖標來訪問它們喜愛的應用。
    PWA賦予了我們創建同時擁有以上兩種優勢的應用的能力。
    這並不是一個新概念——這樣的想法在過去已經在web平台上通過許多方法出現了多次。漸進式增強和響應式設計已經可以讓我們構建對移動端友好的網站。在多年以前的Firefox OS的生態系統中離線運行和安裝web應用已經成為了可能。
    PWAs, 不但如此,更是提供了所有的甚至是更多的特性,來讓web更加優秀。

    引用自MDN

    說人話就是PWA可以讓你的web程序跟一般應用一樣運行,有桌面圖標,能離線,沒有瀏覽器地址欄,一切看起來想個普通的程序/APP。

    新建Blazor PWA程序

    使用VS新建一個Blazor程序,選擇Webassembly模式,勾選支持PWA。

    支持PWA的Blazor程序主要是多了幾個東西:

    1. manifest.json
    2. service-worker.js

    manifest.json

    manifest.json是個清單文件,當程序被安裝到設備上的時候會讀取裏面的信息,名稱是什麼,圖標是什麼,什麼語言等等。

    {
      "name": "BlazorPWA",
      "short_name": "BlazorPWA",
      "start_url": "./",
      "display": "standalone",
      "background_color": "#ffffff",
      "theme_color": "#03173d",
      "icons": [
        {
          "src": "icon-512.png",
          "type": "image/png",
          "sizes": "512x512"
        }
      ]
    }
    
    

    service-worker.js

    service-worker用來跑一些後台任務。它跟瀏覽器主進程是隔離的,也就是說跟原來的JavaScript運行時是分開,當然了它不會阻塞頁面。我們可以用它來完成一些功能,比如對所有的fetch/xhr請求進行過濾,哪些請求走緩存,哪些不走緩存;比如在後台偷偷給你拉一些數據緩存起來。

    // Caution! Be sure you understand the caveats before publishing an application with
    // offline support. See https://aka.ms/blazor-offline-considerations
    
    self.importScripts('./service-worker-assets.js');
    self.addEventListener('install', event => event.waitUntil(onInstall(event)));
    self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
    self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
    
    const cacheNamePrefix = 'offline-cache-';
    const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
    const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/ ];
    const offlineAssetsExclude = [ /^service-worker\.js$/ ];
    
    async function onInstall(event) {
        console.info('Service worker: Install');
    
        // Fetch and cache all matching items from the assets manifest
        const assetsRequests = self.assetsManifest.assets
            .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
            .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
            .map(asset => new Request(asset.url, { integrity: asset.hash }));
        await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
    }
    
    async function onActivate(event) {
        console.info('Service worker: Activate');
    
        // Delete unused caches
        const cacheKeys = await caches.keys();
        await Promise.all(cacheKeys
            .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
            .map(key => caches.delete(key)));
    }
    
    async function onFetch(event) {
        let cachedResponse = null;
        if (event.request.method === 'GET') {
            // For all navigation requests, try to serve index.html from cache
            // If you need some URLs to be server-rendered, edit the following check to exclude those URLs
            const shouldServeIndexHtml = event.request.mode === 'navigate';
    
            const request = shouldServeIndexHtml ? 'index.html' : event.request;
            const cache = await caches.open(cacheName);
            cachedResponse = await cache.match(request);
        }
    
        return cachedResponse || fetch(event.request);
    }
    
    

    項目里有2個service-worker.js文件,一個是開發時候的沒邏輯,還有一個是發布時候的有一些緩存的邏輯。

    運行一下

    如果是PWA程序,在瀏覽器地址欄有個+號一樣的圖標,點擊可以把程序安裝到本地。

    安裝完了會在桌面生成一個圖標,打開會是一個沒有瀏覽器地址欄的界面。

    這樣一個PWA程序已經可以運行了。

    離線運行

    如果只是這樣,僅僅是沒有瀏覽器地址欄,那PWA也太沒什麼吸引力了。個人覺得PWA最大的魅力就是可以離線運行,在沒有網絡的情況下依然可以運行,這樣才像一個原生編寫的程序。

    修改service-worker

    離線的原理也很簡單,就是請求的數據都緩存起來,一般是緩存Get請求,比如各種頁面圖片等。

    // In development, always fetch from the network and do not enable offline support.
    // This is because caching would make development more difficult (changes would not
    // be reflected on the first load after each change).
    
    self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
    self.addEventListener('install', event => event.waitUntil(onInstall(event)));
    
    async function onInstall(event) {
        console.info('Service worker: Install');
    }
    
    
    async function onFetch(event) {
        let cachedResponse = null;
        const cache = await caches.open('blazor_pwa');
        if (event.request.method === 'GET') {
            const request = event.request;
            cachedResponse = await caches.match(request);
            if (cachedResponse) {
                return cachedResponse;
            }
            var resp = await fetch(event.request)
            cache.put(event.request, resp.clone());
            return resp;
        }
    
        return fetch(event.request);
    }
    

    修改一下sevice-worker.js,把GET請求全部緩存起來。這裏為了演示圖方便,其實情況顯然不會這麼簡單粗暴。為了能緩存頁面,顯然必須先在線運行成功一次。

    模擬離線

    當我們修改完上面的js,然後在線正常一次后,可以看到所有GET請求的資源都被緩存起來了。

    我們可以用chrome來模擬離線情況:

    選擇offline模式,然後刷新我們的頁面,如果依然可以正常運行則表示可以離線運行。

    總結

    使用Blazor可以快速的開發PWA應用。利用PWA跟Blazor Webassembly的特性,可以開發出類似桌面的應用程序。或許這是跨平台桌面應用開發除了electron的又一種方案吧。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • 深入理解JVM(③)虛擬機的類加載時機

    深入理解JVM(③)虛擬機的類加載時機

    前言

    Java虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這個過程被稱為虛擬機的類加載機制。

    類加載的時機

    一個類型從被加載到虛擬機內存中開始,到卸載除內存為止,它的生命周期將會經歷加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和 卸載(Unloading)、七個階段,其中驗證、準備、解析三個部分統稱為連接(Linking)。
    類的生命周期如下圖:

    其實加載、驗證、準備、初始化和卸載這五個階段的順序是確定的,類型的加載過程必須按照這種順序按部就班地開始,而解析階段則不一定:它在某些情況下可以在初始化階段之後再開始,這是為了支持Java語音的運行時綁定特性(也稱為動態綁定或晚期綁定)。
    在什麼情況下需要開始類加載過程的第一個階段“加載”,《Java虛擬機規則》中並沒有進行強制約束,但是對於初始化階段《Java虛擬機規範》則是嚴格規定了有且只有以下六種情況必須立即對類進行“初始化”。

    1. 遇到newgetstaticputstaticinvokestatic這四條字節碼指令時,如果類型沒有進行過初始化,則需要先觸發其初始化階段。
      涉及到這四條指令的典型場景有:
    • 使用new關鍵字實例化對的時候。
    • 讀取或設置一個類型的靜態字段(被final修飾、已在編譯期把結果放入常量池的靜態字段除外)的時候。
    • 調用一個類型的靜態方法的時候。
    1. 使用 java.lang.reflect 包的方法對類型進行反射調用的時候,如果類型沒有進行過初始化,則需要先觸發其初始化。
    2. 當初始化類型的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。
    3. 當虛擬機啟動時,用戶需要指定一個要執行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
    4. 當使用JDK7新加入的動態語言支持時,如果一個java.lang.invoke.MethodHandle實例最後的解析結果為REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四種類型的方法句柄,並且這個方法句柄對應的類沒有進行過初始化,則需要先觸發其初始化。
    5. 當一個接口中定義了JDK8新加入的默認方法(被default關鍵字修飾的接口方法)時,如果這個接口的實現類發生了初始化,那該接口要在其之前被初始化。
      除了以上的這個六種場景外,所有引用類型的方式都不會觸發初始化,稱為被動引用。
      下面來看一下哪些是被動引用:

    例子1:

    父類

    package com.jimoer.classloading;
    
    /**
     * @author jimoer
     * @date Create in 2020/06/24 16:08
     * @description 通過子類引用父類的靜態字段,不會導致子類初始化。
     */
    public class FatherClass {
    
        static {
            System.out.println("FatherClass init!!!!!");
        }
    
        public static int value = 666;
    
    }
    

    子類

    package com.jimoer.classloading;
    
    public class SonClass extends FatherClass{
    
        static {
            System.out.println("SonClass init!!!");
        }
    
    }
    

    測試類

    @Test
    public void testInitClass(){
        System.out.println(SonClass.value);
    }
    

    運行結果:

    FatherClass init!!!!!
    666
    

    通過運行結果我們看到,只輸出了“FatherClass init!!!!!”,並沒有輸出“SubClass init!!!”,這是因為對於使用靜態字段,只有直接定義這個字段的類才會被初始化,因此通過子類來引用父類中定義的靜態字段,並不會初始化子類。

    例子2:

    /**
     * 通過數組定義來引用類,不會觸發此類的初始化
     */
    @Test
    public void testInitClass2(){
        FatherClass[] fathers = new FatherClass[5];
    }
    

    運行結果:未打印任何信息。
    通過運行結果我們發現,並沒有打印出 FatherClass init!!!!! ,這說明並沒有觸發Father類的初始化階段。但是這段代碼裏面觸發了另一個名為“[Lcom.jimoer.classloading.FatherClass”的類的初始化階段,它是一個由虛擬機自動生成的、直接繼承與java.lang.Object的子類,創建動作由字節碼newarray觸發。這個類代表了一個元素類型為FatherClass的一維數組,數組中應用的屬性和方法(length屬性和clone()方法)都實現在這個類里。

    例子3:

    /**
     * @author jimoer
     * 常量在編譯階段會存入調用類的常量池中,
     * 本質上沒有直接引用到定義常量的類,
     * 因此不會觸發定義常量的類的初始化。
     */
    public class ConstantClass {
        
        static {
            System.out.println("ConstantClass init !!!");
        }
        
        public static final String CLASS_LOAD = "class load test !!!";
        
    }
    

    使用

    /**
     * 使用常量
     */
    @Test
    public void testInitClass3(){
        System.out.println(ConstantClass.CLASS_LOAD);
    }
    

    運行結果:

    class load test !!!
    

    通過運行結果,我們看到當在使用一個類的常量時,並不會初始化定義了常量的類。這是因為雖然在Java源碼中確實引用了ConstatClass的類的常量CLASS_LOAD,但其實在編譯階段通過常量傳播優化,已經將此常量的值“class load test !!!”直接存儲在使用常量的類中的常量池中了,所以在使用ConstantClass.CLASS_LOAD時候,實際上都被轉化為在使用類自身的常量池的引用了。

    接口也是有初始化過程的,上面的代碼都是用靜態語句塊“static {}”來輸出初始化信息的,而接口中不能使用static{}語句塊,但編譯器仍然會為接口生成“ ()”類構造器,用於初始化接口中所定義的成員變量。
    還有一點接口與類不同,當一個類在初始化時,要求其父類全部都已經初始化過了,但是在一個接口初始化時,並不要求其父接口全部都完成了初始化,只有在真正使用到父接口的時候(例如引用接口中的常量)才會初始化。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※別再煩惱如何寫文案,掌握八大原則!

  • 算法崗面試題:模型的bias和variance是什麼?用隨機森林舉例

    算法崗面試題:模型的bias和variance是什麼?用隨機森林舉例

    校招在即,準備準備一些面試可能會用到的東西吧。希望這次面試不會被掛。

    基本概念

    說到機器學習模型的誤差,主要就是bias和variance。

    • Bias:如果一個模型的訓練錯誤大,然後驗證錯誤和訓練錯誤都很大,那麼這個模型就是高bias。可能是因為欠擬合,也可能是因為模型是弱分類器。

    • Variance:模型的訓練錯誤小,但是驗證錯誤遠大於訓練錯誤,那麼這個模型就是高Variance,或者說它是過擬合。

    這個圖中,左上角是低偏差低方差的,可以看到所有的預測值,都會落在靶心,完美模型;

    右上角是高偏差,可以看到,雖然整體數據預測的好像都在中心,但是波動很大。

    【高偏差vs高方差】
    在機器學習中,因為偏差和方差不能兼顧,所以我們一般會選擇高偏差、低方差的左下角的模型。穩定性是最重要的,寧可所有的樣本都80%正確率,也不要部分樣本100%、部分50%的正確率。個人感覺,穩定性是學習到東西的體現,高方差模型與隨機蒙的有什麼區別?

    隨機森林為例

    上面的可能有些抽象,這裏用RandomForest(RF)來作為例子:
    隨機森林是bagging的集成模型,這裏:
    \(RF(x)=\frac{1}{B}\sum^B_{i=1}{T_{i,z_i}(x)}\)

    • RF(x)表示隨機森林對樣本x的預測值;
    • B表示總共有B棵樹;
    • \(z_i\)表示第i棵樹所使用的訓練集,是使用bagging的方法,從所有訓練集中進行行採樣和列採樣得到的子數據集。

    這裏所有的\(z\),都是從所有數據集中隨機採樣的,所以可以理解為都是服從相同分佈的。所以不斷增加B的數量,增加隨機森林中樹的數量,是不會減小模型的偏差的。
    【個人感覺,是因為不管訓練再多的樹,其實就那麼多數據,怎麼訓練都不會減少,這一點比較好理解】

    【RF是如何降低偏差的?】
    直觀上,使用多棵樹和bagging,是可以增加模型的穩定性的。怎麼證明的?

    我們需要計算\(Var(T(x))\)
    假設不同樹的\(z_i\)之間的相關係數為\(\rho\),然後每棵樹的方差都是\(\sigma^2\).

    先複習一下兩個隨機變量相加的方差如何表示:
    \(Var(aX+bY)=a^2 Var(X)+b^2 Var(Y) + 2ab cov(X,Y)\)

    • Cov(X,Y)表示X和Y的協方差。協方差和相關係數不一樣哦,要除以X和Y的標準差:
      \(\rho=\frac{cov(X,Y)}{\sigma_X \sigma_Y}\)

    下面轉成B個相關變量的方差計算,是矩陣的形式:

    很好推導的,可以試一試。

    這樣可以看出來了,RF的樹的數量越多,RF方差的第二項會不斷減小,但是第一項不變。也就是說,第一項就是RF模型偏差的下極限了。

    【總結】

    • 增加決策樹的數量B,偏差不變;方差減小;
    • 增加決策樹深度,偏差減小;\(\rho\)減小,\(\sigma^2\)增加;
    • 增加bagging採樣比例,偏差減小;\(\rho\)增加,\(\sigma^2\)增加;

    【bagging vs boost】
    之前也提到過了boost算法:
    一文讀懂:GBDT梯度提升
    GBDT中,在某種情況下,是不斷訓練之前模型的殘差,來達到降低bias的效果。雖然也是集成模型,但是可以想到,每一個GBDT中的樹,所學習的數據的分佈都是不同的,這意味着在GBDT模型的方差會隨着決策樹的數量增多,不斷地增加。

    • bagging的目的:降低方差;
    • boost的目的:降低偏差

    喜歡的話請關注我們的微信公眾號~【你好世界煉丹師】。

    • 公眾號主要講統計學,數據科學,機器學習,深度學習,以及一些參加Kaggle競賽的經驗。
    • 公眾號內容建議作為課後的一些相關知識的補充,飯後甜點。
    • 此外,為了不過多打擾,公眾號每周推送一次,每次4~6篇精選文章。

    微信搜索公眾號:你好世界煉丹師。期待您的關注。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※教你寫出一流的銷售文案?

  • 厄瓜多總統與原住民領袖達協議 化解反撙節示威

    摘錄自2019年10月14日中央社報導

    莫雷諾為向國際貨幣基金貸款42億美元而取消燃油補貼後,國內物價迅速飆升,引發長達12天的示威,造成7人喪命。暴力衝突迫使莫雷諾將政府遷移至厄瓜多第2大城瓜亞基爾(Guayaquil),並嚴重影響石油業,能源部暫停超過2/3原油的配送。抗議人士甚至佔領亞馬遜雨林地區的3處石油設施。

    厄瓜多總統莫雷諾及原住民領袖瓦爾加斯13日達成協議,終結近2週來反對撙節措施的暴力抗議。政府是為為獲得國際貨幣基金(IMF)數十億美元貸款,而採取這些緊縮措施。

    法新社報導,聯合國官員代表宣讀的聯合聲明說,「根據這項協議,厄瓜多各地的群眾動員劃下句點,我們承諾會恢復國內和平」。聲明並表示,政府已撤回取消燃油補貼的命令。臉上塗著油彩、頭上頂著羽毛頭飾的瓦爾加斯證實:「這些適用於我們全國各地的措施已經取消了。」

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※教你寫出一流的銷售文案?

  • MapReduce 論文閱讀筆記

    MapReduce 論文閱讀筆記

    目錄

    • Abstract
    • Introduction
    • 2 Programming Model
      • 2.1 Example
      • 2.2 Types
      • 2.3 More Examples
    • 3 Implementation
      • 3.1 Execution Overview
      • 3.2 Master Data Structures
      • 3.3 Fault Tolerance
        • Worker Failure (工作節點故障)
        • Master Failure(主節點故障)
        • Semantics in the Presence of Failures(語義可能存在的故障)
      • 3.4 Locality
      • 3.5 Task Granularity
      • 3.6 Backup Tasks
    • 4 Refinements
      • 4.1 Partitioning Function
      • 4.2 Ordering Guarantees
      • 4.3 Combiner Function
      • 4.4 Input and Output Types
      • 4.5 Side-effects
      • 4.6 Skipping Bad Records
      • 4.7 Local Execution
      • 4.8 Status Information
      • 4.9 Counters
    • Others

    Abstract

    MapReduce :

    • programming model 編程模型
    • an associated implementation for processing and generating large data sets.

    用戶只需要指定 Map(Map函數將 key/value 類型的 pair 生成中間結果的 pair) 和 Reduce 函數(Reduce 函數將所有具有相同中間結果的值組合起來)即可。

    MapReduce 封裝隱藏了分佈式系統并行計算的細節:

    • 輸入數據的分割
    • 計劃將程序分配到一組計算機中
    • 處理機器故障
    • 管理集群內部的通信

    程序(in functional style)分佈式的運行在大型分佈式的集群上,而且具有很好的可伸縮性 scalable。

    Introduction

    過去這些年,Google一直在尋找方法來實現處理大量數據(抓取到的文件,web日誌等)的方法,通常數據量很大而且必須分散在數以千計的電腦上來進行運算。為了處理如何使計算相互關聯,分配數據以及處理故障的問題,往往編寫大量的複雜代碼掩蓋了他們,最初的簡單計算的初衷卻被忽略掉。

    為了解決這種複雜性,抽象出了一個簡單的計算模型放到一個庫中,這個庫隱藏了可能出現的問題:

    • 并行計算
    • 容錯
    • 數據分發
    • 負載均衡

    這個抽象受到了 Lisp 以及很多函數式編程語言中存在的原語 mapreduce 的啟發。

    大多數并行計算都包含兩個步驟:

    • map:將每個邏輯記錄變成 key/value 的中間形式方便計算
    • reduce:將所有具有相同 key 的值組合到一起來進行合適的處理

    我們使用一個函數式的編程模型(functional programming model)可以讓處理大型的并行計算和使用重新執行作為容錯的主要機制變得很簡單。

    這項工作的主要貢獻是:提供了一個簡單但是很強大的接口(interface)讓自動化的并行計算和大規模計算的分發成為可能,結合該接口的實現,可以在商用機的大型集群上實現高性能。

    Section2 :描述了基本的編程模型給出幾個例子

    Section3 :描述MapReduce 接口針對集群運算環境的實現

    Section4 :一些針對該模型的細微的改良

    Section5 :針對實現設計出一系列性能衡量方法

    Section6 :MapReduce 在 Google 中的使用,以及使用 MapReduce 來重寫生產環境的索引系統

    Section7 :相關以及未來的工作

    2 Programming Model

    input: a set of key/value pairs

    output: a set of key/value pairs

    MapReduce 的用戶將只會使用兩個函數 MapReduce

    Map:用戶編寫,將輸入的 pair 變成 k/v 的中間 pairs,然後 MapReduce 會把具有相同 key 的 pair 送給 Reduce 函數

    Reduce: 用戶編寫,接受中間結果 key 和 key 的一系列值。將這些值組合起來成為更少的 k/v;通常每個 Reduce 函數只輸出一個 或者 0 個值。中間結果太多無法全部放到內存中,可以通過迭代的方法來處理大量的 value

    2.1 Example

    設想一個需要統計文件中每個單詞數量的一個問題,我們很可能編寫這樣的代碼:

    map(String key, String value):
    	// key: document name
    	// value: document contents
    	for each word w in value:
    		EmitIntermediate(w, "1")
          
    reduce(String key, String values):
    	// key: a word
    	// values: a list of counts
    	int result = 0
      for each v in values
        result += ParseInt(v)
      Emit(AsString(result))
    

    map:給每個單詞添加一個屬性(出現的次數,這裏就是1)

    reduce:給每個特定的單詞加起來計算總數並且提交

    此外,用戶編寫代碼以使用輸入和輸出文件的名稱以及可選的調整參數來填充mapreduce規範對象。然後,用戶調用MapReduce函數,並將其傳遞給指定對象。用戶代碼與MapReduce庫(在C ++中實現)鏈接在一起。

    2.2 Types

    儘管前面偽代碼使用 string 來寫的輸入輸出,但是從概念上說,是由用戶來指定 map 和 reduce 的類型

    map (k1, v1)  ->  list(k2, v2)
    map (k2, list(v2)) -> list(v2)
    

    輸入值和中間值來自不同的域,中間值和輸出值來自相同的域

    C ++實現在用戶定義的函數之間來回傳遞字符串,並將其留給用戶代碼以在字符串和適當的類型之間進行轉換

    2.3 More Examples

    這有一些可以使用 MapReduce 簡化的計算:

    Distributed Grep: 分佈式的匹配,map 函數提交一個符合匹配的 line, reduce 的作用只是複製中間結果到輸出

    Count of URL Access Frequency: 網頁訪問計數,map 處理網頁請求並且輸出中間結果為 <URL, 1>, reduce 功能是將所有的相同的 URL 計算到一起提交為 <URL, total count>

    Reverse Web-Link Graph: 翻轉網絡鏈接圖, map 輸出 <target, source> pairs ,將 target 命名為 source。reduce函數連接與給定目標URL關聯的所有源URL的列表,並提交該對 <target, list(source)>

    Term-Vector per Host: 術語向量是出現在一篇文章中最重要的術語集合列表<word, frequency> pairs。map 函數給每個輸入文件輸出一個 <hostname, term vector> pairs,reduce 函數傳遞給特定主機的術語向量,然後去掉不常出現的向量最後提交一個 <hostname, term vector> pair

    Invert Index:map函數解析每個文檔,併發出一系列<單詞,文檔ID>對。 reduce函數接受給定單詞的所有對,對相應的文檔ID進行排序,併發出一個“單詞,列表(文檔ID)”對。setofall輸出對形成一個簡單的倒排索引。易於擴展此計算以跟蹤單詞位置。

    Distributed Sort: 分佈式排序,map函數功能從每個記錄中提取鍵,併發出一個<key, record>對。 reduce函數將所有對保持不變。這種計算取決於第4.1節中描述的分區功能和第4.2節中描述的排序屬性。

    3 Implementation

    MapReduce 可以有很多不同的實現,正確實現是根據你自己所在的環境來進行實現,例如某個實現可能很適合一個共享內存的小機器,某個實現可能是在NUMA多處理器的環境下,也可能是在一個大的網絡連接的集群的機器中。

    NUMA Non-uniform memory access

    非統一內存訪問架構是一種為多處理器的電腦設計的內存架構,內存訪問時間取決於內存相對於處理器的位置。在NUMA下,處理器訪問它自己的本地內存的速度比非本地內存快一些。 非統一內存訪問架構的特點是:被共享的內存物理上是分佈式的,所有這些內存的集合就是全局地址空間

    這節描述的是 Google 雲計算環境下廣泛使用的,下面是 Google 的配置:

    1. 機器,都是典型的Linux系統,運行在基於x86的雙處理器上,每台機器 2-4GB 內存
    2. 網絡,使用商品網絡硬件,在機器級別通常為100Mb/s或1Gb/s,但平均平均對分帶寬要小得多
    3. 集群中有上百或者上千個機器,所以機器故障出現很正常
    4. 存儲,使用廉價的 IDE 硬盤直接保存每個機器自己的數據,開發出的分佈式文件系統來管理這些磁盤上的文件。文件系統使用複製來在不可靠的硬件上提供可用性和可靠性。
    5. 用戶通過一個任務調度系統提交任務。每個工作包含一系列的任務,使用任務調度器來分配到集群中可用的機器上

    3.1 Execution Overview

    Map 調用分佈在多個機器上,自動將輸入數據分配成 M 組,輸入的分割可以并行的發生在不同的機器上。 Reduce 調用也是分佈式的,通過將中間值的key使用一個分割函數(例如:hash(key) mod R)來將任務分配到不同的機器上。分區數量 R 的取值和分區數量也是通過用戶來指定的。

    下面這個圖說明了 MapReduce 執行的完整流程:

    1. 用戶程序中的 MapReduce 庫首先將輸入文件分成 M 份(每份 16MB – 64 MB),然後開始在集群中複製很多拷貝
    2. 程序中有一份拷貝是特殊的(master)。剩下的 worker 來被分配工作,有 M 個map任務和 R 個 reduce 任務來分配給不同的 worker。master 來挑選空閑的 worker 分配給他們每個一個 map task 或者 reduce task
    3. 一個被分配到 map 工作的 worker 將會從對應的分割的內容中讀取。它會解析 k/v pair 到輸入數據並且傳給到用戶定義的 map 函數。Map 函數產生的中間 key/value 數據將會被保存在內存的緩存中
    4. 緩存中的 pair 會被周期性的寫入到本地磁盤上,通並且過分割函數將該文件分成的 R 個區域。這些緩衝對的位置在本地磁盤上被傳遞迴主服務器,該主服務器負責將這些位置轉發給reduce worker。
    5. 當一個 reduce worker 被 master 告知這些存儲的區域,reduce worker 將使用遠程過程調用來從 map workers 的本地磁盤以及緩存中讀取 pair 對。當一個 reduce worker 讀取了所有的中間數據,它將會跟配相同的 key 來進行排序。排序是必需的因為通常會有很多不同的 key 映射到同一個 reduce 任務。如果中間數據太大來放到內存中排序,外排序就會被使用
    6. reduce worker 迭代排過序的中間數據,對於每個獨特的中間值 key,它會傳遞這個 key 和對應中間值到用戶定義的 reduce 函數中來處理。Reduce函數的輸出將附加到此reduce分區的最終輸出文件中
    7. 當所有的 map task 和 reduce task 都被完成之後,master 喚醒用戶程序。這個時候,MapReduce 的調用返回到用戶的代碼邏輯中

    在成功的完成之後,mapreduce執行的輸出將會在 R 個輸出文件中(每個reduce task 都會被用戶指定文件的名稱)。通常來說,用戶不會將 R 個輸出文件合併成一個,而是將這個文件作為另一個 MapReduce 的輸入。或者把他們當成另外的分佈式程序的輸入

    3.2 Master Data Structures

    master 保存一些數據結構。對於每個 map 和 reduce 任務,它會保存狀態(idle,in-progress,completed),以及識別每個 worker machine(非空閑任務)。

    master 是處於 map 任務發送到 reduce 任務中間的導管,master 會保存中間文件區域的位置。因此,對於每個完成的 map 任務, master 保存 R 個由map任務產生的中間文件的大小和位置。當 map 任務完成之後,會更新這些文件的位置和大小。這些信息將逐漸被推送到已經在工作的 reduce 任務。

    3.3 Fault Tolerance

    由於 MapReduce 是用來在大量機器上處理大量數據的一個庫,所以這個庫必需能夠有很好的容錯能力。

    Worker Failure (工作節點故障)

    master 節點周期性的 ping 每個 worker 節點。如果在一個特定的時間內沒有收到回復,那麼 master 節點就會將這個 worker 標記為失敗。完成 map 任務的 worker 節點將會被重置為 idle 空閑狀態,然後就可以被其他 worker 節點安排。相似的,如果一個節點上的 map 或者 reduce 任務在執行過程中失敗了,那麼這個任務將會被重置然後分配然後重新分配。

    如果一個 map 任務的節點在完成任務之後出現故障,那麼就需要重新執行這個任務,因為這個節點變得不可訪問。但是如果是 reduce 任務完成之後節點出現故障,不需要重新執行,這是因為 reduce 任務的輸出被保存到一個全局文件系統中。

    當一個 map 任務首先在節點 A 上執行之後在節點 B 上執行時,所有正在執行的 reduce 節點將會被告知這次重新執行過程。所有還沒有從 worker A 讀取數據的 reduce 任務將會 worker B 讀取。

    MapReduce 可以處理大規模的 worker 節點故障。例如在一次 MapReduce 任務中,一個由80台計算機組成的集群由於網絡問題無法訪問,MapReduce 的 master 節點只是簡單的讓那些不能正常執行任務的工作節點再次執行任務,然後繼續向前執行任務直到完成 MapReduce 操作。

    Master Failure(主節點故障)

    讓 master 節點周期性的上述master節點的數據結構的檢查點。如果 master task 失敗了,可以從上一個檢查點的拷貝恢復。但是如果只有一個主節點,那麼出現故障的可能性非常小,因此如果主節點出現故障,我們的當前的實現就中止了此次 MapReduce 任務。客戶端可以檢查到這種情況,然後可以選擇是否重試 MapReduce 操作。

    Semantics in the Presence of Failures(語義可能存在的故障)

    當用戶指定的 map 和 reduce 操作對於他們的輸入輸出都確定好了之後,分佈式的實現將會產生一個類似於線性執行過程任務執行的結果。

    我們依賴於 map 和 reduce 任務的 原子性 commit 作為這個特性的保證。每個執行過程中的任務都會將把他的輸出保存到一個私有的臨時文件中。一個 reduce 任務產生一個這樣的文件,但是一個 map 任務將會產生 R 個這樣的文件。當一個 map 任務完成之後, worker 節點發送包含這 R 個文件名的消息到 master 節點。如果這個節點已經接收到完成的消息,那麼將會忽略這個消息,否則將會把這些文件名保存到 master 節點的數據結構中。

    當一個 reduce 任務完成的時候,reduce worker 將會原子性的將它的臨時文件重命名成一個輸出文件。如果有以個 reduce 任務在多個機器上同時完成,那麼這個重命名的操作將會對於一個輸出文件多次執行。我們依賴於基礎文件系統提供的原子重命名操作,以確保最終文件系統狀態僅包含一次執行reduce任務所產生的數據。

    map 和 reduce 操作絕大部分都是確定性的,事實上我們的語義將會和線性順序執行的程序的結果一致,這樣很容易分析程序的行為。當 map/reduce 操作是不確定的時候,我們提供弱化但是可信的語義。例如在一個不確定的語義中,一個特定 reduce 任務的輸出和這個任務順序執行的結果一致。然而,用於不同reduce任務R2的輸出可以對應於由不確定性程序的不同順序執行所產生的用於R2的輸出。(這裏保留疑問,沒有太懂什麼意思,指的是有可能是線性結果一致的意思嗎?)

    考慮有一個 map 任務 M,和兩個 reduce 任務 R1,R2,e(Ri)是 Ri提交的結果,弱一點的語義指的是,e(R1) 可能讀取的 M 的一個執行的結果而 e(R2) 可能讀取的是 R 執行輸出的另外一個結果。

    3.4 Locality

    網絡帶寬是在雲計算環境中比較稀缺的資源(盡量少用)。通過將輸入文件(由GFS保管)保存到本地磁盤上來減少網絡帶寬的使用。GFS 將每個文件分成 64 MB的塊,然後將每塊保存幾個副本(通常為3份)在不同的機器上。MapReduce 盡量將這些位置信息保存下來然後盡量將含有某個文件主機的任務分配給它,這樣就可以減少網絡的傳遞使用。如果失敗,那麼將會嘗試從靠近輸入數據的一個副本主機去啟動這個任務。當在一個集群上執行大型的 MapReduce 操作的時候,輸入數據一般都是本地讀取,減少網絡帶寬的使用。

    3.5 Task Granularity

    我們將一個 map 任務分成 M 塊,然後 reduce 會處理最後輸出成 R 塊。

    理想情況下,M 和 R 應該遠遠大於集群中的 worker 節點數量。讓每個節點執行不同的任務將會有利於動態的負載均衡,同時會加速當一個 worker 節點故障之後的恢復,map 任務完成之後可以分配到所有的其他節點上。

    R 和 M 在實現過程中會有邊界,因為 schedule 決策需要 O(M + R)的時間,保存這個信息到內存中需要 O(M*R)的複雜度(常數內存很小)。

    R 通常是由用戶指定的,這是由於每個 reduce 任務的輸出將會輸出到單獨的文件中。在實際中,通常 M 被選擇到每個單獨的任務輸入數據將會是 16MB ~ 64MB,讓 R 要比我們使用的機器的數目的小几倍。

    例如,M =20,000,R = 5,000,worker machines = 2000

    3.6 Backup Tasks

    讓整個 MapReduce 任務時間延長的原因主要有 “拖延者”:某一個機器在 map/reduce 任務上花費了太多的時間。導致這個 “拖延者” 的原因可能有很多,比如一塊讀寫速度超級慢的硬盤 1MB/s(其他的是 30MB/s)比如集群在這個機器上也分配了其他任務,這些任務競爭使用 CPU、硬盤網絡等等。我們最近遇到的一個問題是機器初始化代碼中的一個錯誤,該錯誤導致禁用了處理器緩存:受影響機器的計算速度降低了一百倍。

    4 Refinements

    儘管上面說的夠用了,但是我們還是找到一些可以優化的點。

    4.1 Partitioning Function

    用戶將會指定輸出文件的數量 R。輸入數據將會根據中間值來把這些數據分區。一個默認的分區函數就是 hash函數(hash(key) mod R) 。通常情況下這樣很好,但是在某些情況下不是很好。例如,輸出數據是 URL key,我們希望具有相同主機 hostname 的 URL 在一起,這樣,MapReduce 提供了用戶自己制定 分區函數的方式,例如可以寫為(hash(Hostname(urlkey))),這樣具有相同的 hostname 的URL將會在一個相同的輸出文件中。

    4.2 Ordering Guarantees

    我們保證了在某個給定的分區中,中間值的 k/v pair 將會按照增序排列,這樣在某些需要有序的場景下是很有用的。

    4.3 Combiner Function

    在某些情況下,最終的輸出文件reduce是需要根據中間值來合併的。例如在2.1的 word count 中,需要統計每個單詞的數目,我們輸出的是 <word, 1> 這樣的形式。這些pair都需要通過網絡來進行發送到 reduce 工作節點,我們提供了一個 combiner 函數,讓用戶可以在發送數據之前執行的函數,也就是說在本地先合併,然後再發送到網絡中去。

    在每個執行 map task 的機器都會執行 conbine 函數。

    combine 和 reduce 的唯一區別:

    • combine 輸出是到中間值文件中
    • reduce 輸出到一個最終的輸出文件中

    4.4 Input and Output Types

    MapReduce 庫提供了幾種輸入的類型。

    例如,在 “text” 模式下輸入將每一個行當作 k/v pair,這行的偏移量當作 key,這行的內容當作 value。

    用戶可以通過實現 reader 接口在實現自己的輸入類型,儘管大多數用戶都只使用預定義的類型。

    reader 不一定要從文件中讀取數據,也可以從數據庫中讀取數據,或者從內存中的某個的某個數據結構中獲取數據。

    同樣的,輸出也可以自定義。

    4.5 Side-effects

    在某些情況下,用戶需要產生一些額外的輸出文件在reduce 的輸出結果中。我們依靠這個應用程序自己的編寫者來使此類副作用成為原子和冪等的。通常應用程序將會寫入一個臨時文件中,然後當它完成的時候將會原子性的重命名這個文件。

    我們不提供原子性的兩節點提交由一個任務產生的多個輸出文件。因此,產生多個輸出的結果的任務應該是確定性的。

    4.6 Skipping Bad Records

    在處理大量數據的時候,由於用戶的 map/reduce 函數的錯誤在處理某些數據的時候產生bug,這個時候可以選擇跳過這些 bug。有的時候我們可以查找到bug所在的地方,但是有的時候我們找不到bug,因為可能是第三方的庫導致的錯誤,我們提供了一種可選的執行模式來跳過這些可能出現錯誤的記錄。

    每個 worker 進程都會有一個監聽段錯誤和總線錯誤的處理器。在執行用戶的 map/reduce 函數之前,mapreduce 將會在一個全局變量中保存順序編號。如果用戶代碼產生了一個 signal,那麼就會發送一個 UDP包到 MapReduce 的 master 節點上。當 master 節點發現在某個幾點上出現了很多次故障的時候,之後就會跳過這個記錄。

    4.7 Local Execution

    在 Map/Reduce 中debug需要一些 trick,因為在分佈式系統中執行可能是在幾千台機器中,工作分配也是動態的。MapReduce 提供了一個另外的本地MapReduce 的本地實現(順序執行),這樣就可以在本地來進行 debug了。

    4.8 Status Information

    master 節點通過 HTTP 服務器提供一個显示當前狀態的網頁。這個界面显示了多少任務完成了,多少任務還在執行,中間數據有多少字節等,還包含了錯誤的標準輸出文件的鏈接等,用戶可以通過這個界面預估還有多久可以完成任務。當執行很慢的時候,可以通過這個界面來查找原因。

    另外,top-level 的狀態信息還會显示哪些 worker 節點有故障,哪些任務失敗了。

    4.9 Counters

    提供一個全局的計數器來統計某些數據,例如統計大寫單詞的出現次數。

     Counter* uppercase;
      uppercase = GetCounter("uppercase");
      map(String name, String contents):
        for each word w in contents:
    			if (IsCapitalized(w)): 
    				uppercase->Increment(); 
    			EmitIntermediate(w, "1");
    

    計數器的值會從每個節點周期性的發送到master節點,主節點統計計數器的值並且在 狀態頁面显示。

    有些值MapReduce 會自己去統計。

    計數器功能對於完整性檢查MapReduce操作的行為很有用。例如,在某些MapReduce操作中,用戶代碼可能想要確保所生成的輸出對的數量完全等於所處理的輸入對的數量,或者所處理的德語文檔的比例在該比例之內。

    Others

    shuffle:將所有具有相同 key 的value 發送個單個的 reduce 進程,在網絡上傳輸數據,是MapReduce代價最大的部分

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※別再煩惱如何寫文案,掌握八大原則!

  • springboot_自動配置原理

    springboot_自動配置原理

    目錄

    • 1.1 @SpringBootApplication
    • 2.1 @EnableAutoConfiguration
      • 2.1.1 @AutoConfigurationPackage
      • 2.1.2 @Import({Registrar.class})
    • 3.1 以HttpEncodingAutoConfiguration為例

    springboot啥都不難,總所周知spring全家桶系列難就難在理解源碼。。。。。。。

    今天結合網上資料,自己總結了一下springboot的自動配置原理。

    我現在使用的springboot版本為2.3.1.不同版本的springboot在源碼上有差別!但大體一致。

    管他三七二十一先打個斷點再說:

    1.1 @SpringBootApplication

    這個註解點進去我們可以看到:

    這裏面主要關注兩個東西:

    • @SpringBootConfiguration
    • @EnableAutoConfiguration
      第一個註解點進去:

      可以看到這個@SpringBootConfiguration本質就是一個@Configuration,標註在某個類上,表示這是一個Spring Boot的配置類。
      第二個註解@EnableAutoConfiguration: 開啟自動配置類,SpringBoot的精華所在。(最重要的就是這個註解)

    2.1 @EnableAutoConfiguration

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import({AutoConfigurationImportSelector.class})
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
        Class<?>[] exclude() default {};
    
        String[] excludeName() default {};
    }
    

    兩個比較重要的註解:

    • @AutoConfigurationPackage:自動配置包。
    • @Import({AutoConfigurationImportSelector.class}):導入自動配置的組件。

    2.1.1 @AutoConfigurationPackage

    點進去瞅瞅:

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import({Registrar.class})
    public @interface AutoConfigurationPackage {
        String[] basePackages() default {};
    
        Class<?>[] basePackageClasses() default {};
    }
    

    發現這裡有導入Regitstrar類:

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
            Registrar() {
            }
    
            public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
            }
    
            public Set<Object> determineImports(AnnotationMetadata metadata) {
                return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
            }
        }
    

    new PackageImport(metadata).getPackageName(),它其實返回了當前主程序類的 **同級以及子級 ** 的包組件。

    什麼意思呢?

    我們來看這樣一個目錄:

    bean1和我們的springboot啟動類位於同一個包下,二bean2不是位於我們啟動類的同級目錄或者子級目錄,那麼我們啟動的時候bean2是不會被加載到的!所以你項目的一切需要加入容器的類必須放在啟動類的同級包下或者它的子級目錄中。

    2.1.2 @Import({Registrar.class})

    AutoConfigurationImportSelector有一個方法為:selectImports。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
                return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
            }
        }
    

    它首先回去檢查是否開啟了自動配置類,然後才回去加載註解數據 this.getAutoConfigurationEntry(annotationMetadata);
    那麼這個annotationMetadata在哪兒?
    來看下面一行代碼:

      protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
            Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
            return configurations;
        }
    

    SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    再點進去我們會發現這一行代碼:
    Enumeration urls = classLoader != null ? classLoader.getResources(“META-INF/spring.factories”) : ClassLoader.getSystemResources(“META-INF/spring.factories”);

    它其實是去加載 public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;外部文件。這個外部文件,有很多自動配置的類。如下:

    spring.factories文件由一組一組的key=value的形式,其中一個key是EnableAutoConfiguration類的全類名,而它的value是一個xxxxAutoConfiguration的類名的列表,這些類名以逗號分隔。
    springboot項目啟動時,@SpringBootApplication用在啟動類在SpringApplication.run(…)的內部就會執行selectImports()方法,找到所有JavaConfig自動配置類的全限定名對應的class,然後將所有自動配置類加載到Spring容器中。

    3.1 以HttpEncodingAutoConfiguration為例

    @Configuration(
        proxyBeanMethods = false
    )    //表示是一個配置類,可以給容器中添加組件
    @EnableConfigurationProperties({ServerProperties.class})// 啟用ConfigurationProperties功能
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )
    @ConditionalOnClass({CharacterEncodingFilter.class})
    @ConditionalOnProperty(
        prefix = "server.servlet.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )
    

    @EnableConfigurationProperties({ServerProperties.class})// 啟用ConfigurationProperties功能
    ServerProperties.class:

    @ConfigurationProperties(
        prefix = "server",
        ignoreUnknownFields = true
    )
    

    @ConditionalOnWebApplication :spring底層@Conditional註解,根據不同的條件進行判斷,如果滿足條件整個配置類才會生效。

    總結:
    1.springboot會自動加載大量的自動配置類。
    2.只要我們要用的組件有,我們就不需要再去配置
    3.給容器添加組件的時候。會從properties類中獲取某些屬性。我們就可以在配置文件中指定這些屬性。
    xxxxxAutoConfiguration:自動配置類

    給容器中添加屬性:
    xxxxProperties:封裝配置文件中的相關屬性。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※超省錢租車方案

    ※教你寫出一流的銷售文案?

  • BMW 高階電動車 i3 與 i8 正式登陸

    BMW Corp  21 日首度針對中國大陸消費者行銷純電動車「i3 」,讓當地剛剛萌芽的高階電動車市場競爭開始升級。   華爾街日報 22 日報導,i3 21 日在大陸正式開賣、定價約 73,000 美元。i3 是 BMW 在大陸第一款純電動車,電動車製造商特斯拉 (Tesla) 才剛在 4 月份將大陸第一台「Model S」電動車交給當地顧客。Model S 在大陸的定價約 121,000 美元。   BMW 雖然並未透露正式的銷售目標,但該公司中國分部執行長 Karsten Engel 曾在 4 月表示,BMW 希望 2014 年能在當地賣出 1,000 台左右的 i3 與 i8。BMW 目前已經在北京、上海、深圳以及瀋陽指定了 7 家經銷商。   特斯拉成長動能逐漸趨緩,得在亞洲(尤其是大陸)加把勁,才能達成 2014 年預設的銷售目標。現在 BMW 又來搶食大餅,特斯拉可得趕緊擬定對策了。  

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 高盛:再下60億美元資本 特斯拉就能顛覆汽車界

    上周,高盛分析師派翠克•阿爾尚博(Patrick Archambault)指出,特斯拉或許需要再投入60億美元現金(尤其在2017年至2025年之間),才能實現“到2020年電動汽車的年產量達到50萬輛”的目標,從而顛覆汽車市場。   而特斯拉希望在明年年底前交付10萬輛電動汽車,其中包括Model S和即將推出的Model X。阿爾尚博預測,特斯拉要實現既定目標,到2025年為止將需要生產180萬至320萬輛電動汽車。但是,屆時如果特斯拉未能實現這些生產目標,電池工廠所生產的多餘電池將可能最終由太陽能公司SolarCity所消化。   特斯拉已經為SolarCity生產了少量的蓄電元件,超級電池工廠竣工後將滿足SolarCity部分或全部的耗電需求。馬斯克表示,兩家公司能以獨特的方式相互相容。如果更多的美國人能接受太陽能用於住宅和商業供電,特斯拉及其電池製造合作夥伴松下就能把超級電池工廠過剩產能所餘下的蓄電元件提供給SolarCity。超級電池工廠每年將為特斯拉生產50萬件電池組,因此,特斯拉不打算成為SolarCity蓄電組件的主要供應商。但如果特斯拉達不到生產目標,超級電池工廠就可能為SolarCity多出出力。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • F-貿聯營收隨 Tesla 出貨倍增 2015 年可望翻倍成長

    從 2005 年開始就與美國電動車大廠 Tesla 合作,而貿聯的營收比重也隨著 Tesla 成長,從 2013 年的 3~5%,到 2014 年倍增到 8% 至 10%,有機會晉升貿聯的前五大客戶。   據了解,2013 年同期貿聯打入 Tesla 的零件料號總數約 45 個,目前已倍增到 100 個,法人推估,平均貿聯交貨每台電動車用線的成本可達 300~400 美元,以 2015 年 Tesla 出貨量上看 10  萬輛來看,2015 年對貿聯的營收貢獻可望維持近倍成長,營收占比拉高至 15~20%。  

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※別再煩惱如何寫文案,掌握八大原則!

  • 重歐美輕亞洲 特斯拉第200個充電站投入使用

    近日,特斯拉宣佈其第200個超級充電站已開放使用。這個超級充電站位於美國加州南部的奧克斯納德河流公園公務中心附近。特斯拉目前有57%的充電站位於美國。這些超級充電站可讓特斯拉電動汽車30分鐘充滿電。   特斯拉正不斷完善其充電網路,其向南有霍索恩(Hawthorne)和特甬牧場(Tejon Ranch)超級充電站,向北有索爾萬(Solvang)超級充電站,東部則有巴斯托(Barstow)超級充電站。他們表示明年其充電站網路將覆蓋美國98%的區域。   目前特斯拉已分別在北美、歐洲和亞洲建造了114座、66座、20座超級充電站。為了完善充電網路,特斯拉未來將在北美進一步縮小超級充電站間的間隙,並且將其將延伸至加拿大,而在歐洲,特斯拉將增大超級充電站密度和覆蓋國家。但是在亞洲,特斯拉的超級充電站建造計畫則顯得相對保守,如今在中國僅有7座。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※教你寫出一流的銷售文案?