標籤: 鏡頭 收購

  • 大規模搜索廣告的端到端一致性實時保障

    大規模搜索廣告的端到端一致性實時保障

    一、背景

    電商平台的搜索廣告數據處理鏈路通常較長,一般會經歷如下過程:

    • 廣告主在後台進行廣告投放;
    • 投放廣告品及關鍵詞數據寫入數據庫;
    • 數據庫中的數據通過全量構建(導入數據倉庫再進行離線批處理)或增量構建(藉助消息隊列和流計算引擎)的方式產出用於構建在線索引的“內容文件”;
    • BuildService基於“內容文件”,構建出在搜索服務檢索時使用的索引。

    下圖是ICBU的廣告系統的買賣家數據處理鏈路:

    右半部分(BP->DB)和offline部分即為廣告投放數據的更新過程。

    複雜的數據處理鏈路結合海量(通常是億級以上)的商品數據,對線上全量商品的投放狀態正確性測試提出巨大挑戰。從數據庫、到離線大規模數據聯表處理、到在線索引構建,鏈路中的任一節點出現異常或數據延遲,都有可能會對廣告主以及平台造成“資損”影響,例如:

    • 廣告主在後台操作取消A商品的廣告投放,但是因為數據鏈路處理延遲,搜索引擎中A的狀態仍處於“推廣中”,導致A能繼續在買家搜索廣告時得到曝光,相應地當“點擊”行為發生時,造成錯誤扣款。
    • 廣告主設定某個產品只限定對某個地域/國家的客戶投放廣告,但是因為搜索引擎的過濾邏輯處理不恰當,導致客戶的廣告品在所有地區都進行廣告投放,同樣會造成錯誤點擊扣款。

    傳統的測試手段,或聚焦於廣告主後台應用的模塊功能測試,或聚焦於搜索引擎的模塊功能測試,對於全鏈路功能的測試缺乏有效和全面的測試手段。而線上的業務監控,則側重於對業務效果指標的監控,如CTR(click through rate,點擊率)、CPC(cost per click,點擊成本)、RPM(revenue per impression,千次瀏覽收益)等。對涉及廣告主切身利益和平台總營收的廣告錯誤投放問題,缺乏有效的發現機制。

    我們期望對在線搜索廣告引擎所有實際曝光的商品,通過反查數據庫中曝光時刻前它的最後狀態,來校驗它在數據庫中的投放狀態與搜索引擎中的狀態的一致性,做到線上廣告錯誤投放問題的實時發現。同時,通過不同的觸發檢測方式,做到數據變更的各個環節的有效覆蓋。

    二、階段成果

    我們藉助日誌流同步服務(TTLog)、海量數據NoSQL存儲系統(Lindorm)、實時業務校驗平台(BCP)、消息隊列(MetaQ)、在線數據實時同步服務(精衛)以及海量日誌實時分析系統(Xflush)實現了ICBU搜索廣告錯誤投放問題的線上實時發現,且覆蓋線上的全部用戶真實曝光流量。同時,通過在數據變更節點增加主動校驗的方式,可以做到在特定場景下(該廣告品尚未被用戶檢索)的線上問題先於用戶發現。

    此外,藉助TTLog+實時計算引擎Blink+阿里雲日誌服務SLS+Xflush的技術體系,實現了線上引擎/算法效果的實時透出。

    下面是ICBU廣告實時質量大盤:

    從八月底開始投入線上使用,目前這套實時系統已經發現了多起線上問題,且幾乎都是直接影響資損和廣告主的利益。

    三、技術實現

    圖一:

    1. 引擎曝光日誌數據處理

    對於電商搜索廣告系統,當一個真實的用戶請求觸達(如圖一中1.1)時,會產生一次實時的廣告曝光,相對應地,搜索引擎的日誌里會寫入一條曝光記錄(如圖一中2)。我們通過日誌流同步服務TTLog對搜索引擎各個服務器節點上的日誌數據進行統一的搜集(如圖一中3),然後藉助數據對賬服務平台BCP對接TTLog中的“流式”數據(如圖一中4),對數據進行清洗、過濾、採樣,然後將待校驗的數據推送到消息隊列服務MetaQ(如圖一中5)。

    2. DB數據處理

    圖二:

    如圖二所示,通常,業務數據庫MySQL針對每個領域對象,只會存儲它當前時刻最新的數據。為了獲取廣告品在引擎中真實曝光的時刻前的最後數據,我們通過精衛監聽數據庫中的每次數據變更,將變更數據“快照”寫入Lindorm(底層是HBase存儲,支持海量數據的隨機讀寫)。

    3. 數據一致性校驗

    在廣告測試服務igps(我們自己的應用)中,我們通過監聽MetaQ的消息變更,拉取MetaQ中待校驗的數據(如圖一中6),解析獲得曝光時每個廣告品在搜索引擎中的狀態,同時獲得其曝光的時刻點。然後基於曝光時刻點,通過查詢Lindorm,獲得廣告品於曝光時刻點前最後在MySQL中的數據狀態(如圖一中7)。然後igps對該次廣告曝光,校驗引擎中的數據狀態和MySQL中的數據狀態的一致性。

    如果數據校驗不一致,則打印出錯誤日誌。最後,藉助海量日誌實時分析系統Xflush(如圖一中8),我們可以做到對錯誤數據的實時聚合統計、可視化展示以及監控報警。

    4. 數據變更節點的主動校驗

    因為線上的實時用戶搜索流量具有一定的隨機性,流量場景的覆蓋程度具有很大的不確定性,作為補充,我們在數據變更節點還增加了主動校驗。

    整個數據鏈路,數據變更有兩個重要節點:

    • MySQL中的數據變更;
    • 引擎索引的切換。

    對於MySQL中的數據變更:我們通過精衛監聽變更,針對單條數據的變更信息,構建出特定的引擎查詢請求串,發起查詢請求(如圖一中1.3)。

    對於引擎索引的切換(主要是全量切換):我們通過離線對歷史(如過去7天)的線上廣告流量進行聚合分析/改寫,得到測試用例請求集合。再監聽線上引擎索引的切換操作。當引擎索引進行全量切換時,我們主動發起對引擎服務的批量請求(如圖一中1.2)。

    上述兩種主動發起的請求,最後都會復用前面搭建的數據一致性校驗系統進行廣告投放狀態的校驗。

    上圖是對廣告投放狀態的實時校驗錯誤監控圖,從圖中我們清晰看到當前時刻,搜索廣告鏈路的數據質量。無論是中美業務DB同步延遲、DB到引擎數據增量處理鏈路的延遲、或者是發布變更導致的邏輯出錯,都會導致錯誤數據曲線的異常上漲。校驗的規則覆蓋了推廣計劃(campaign)、推廣組(adgroup)、客戶狀態(customer)、詞的狀態(keyword)、品的狀態(feed)。校驗的節點覆蓋了曝光和點擊兩個不同的環節。

    5. 引擎及算法的實時質量

    圖三:

    搜索引擎日誌pvlog中蘊含了非常多有價值的信息,利用好這些信息不僅可以做到線上問題的實時發現,還能幫助算法同學感知線上的實時效果提供抓手。如圖三所示,通過實時計算引擎Blink我們對TTLog中的pv信息進行解析和切分,然後將拆分的結果輸出到阿里雲日誌服務SLS中,再對接Xflush進行實時的聚合和可視化展示。

    如上圖所示,上半年我們曾出現過一次線上的資損故障,是搜索應用端構造的搜索廣告引擎SP請求串中缺失了一個參數,導致部分頭部客戶的廣告沒有在指定地域投放,故障從發生到超過10+客戶上報才發現,歷經了10幾個小時。我們通過對SP請求串的實時key值和重要value值進行實時監控,可以快速發現key值或value值缺失的場景。

    此外,不同召回類型、扣費類型、以及扣費價格的分佈,不僅可以監控線上異常狀態的出現,還可以給算法同學做實驗、調參、以及排查線上問題時提供參考。

    四、幾個核心問題

    1. why lindorm?

    最初的實現,我們是通過精衛監聽業務DB的變更寫入另一個新的DB(MySQL),但是性能是一個非常大的瓶頸。我們的數據庫分了5+個物理庫,1000+張分表,單表的平均數據量達到1000+w行,總數據達到千億行。

    后通過存儲的優化和按邏輯進行分表的方式,實現了查詢性能從平均1s到70ms的提升。

    2. why BCP + MetaQ + igps?

    最初我們是想直接使用BCP對數據進行校驗:通過igps封裝lindorm的查詢接口,然後提供hsf接口供在BCP里直接使用。

    但是還是因為性能問題:TTLog的一條message平均包含60+條pv,每個pv可能有5個或更多廣告,每個廣告要查6張表,單條message在BCP校驗需要調用約60x5x6=1800次hsf請求。當我們在BCP中對TTLog的數據進行10%的採樣時,後端服務igps的性能已經出現瓶頸,hsf線程池被打滿,同時7台服務器的cpu平均使用率達到70%以上。

    藉助MetaQ的引入,可以剔除hsf調用的網絡開銷,同時將消息的生產和消費解耦,當流量高峰到達時,igps可以保持自己的消費速率不變,更多的消息可以暫存在隊列里。通過這一優化,我們不僅扛住了10%的採樣,當線上採樣率開到100%時,我們的igps的服務器的平均cpu使用率仍只維持在20%上下,而且metaq中沒有出現消息堆積。

    不過這樣一來,bcp的作用從原來的“採樣、過濾、校驗、報警”,只剩下“採樣、過濾”。無法發揮其通過在線編碼可以快速適應業務變化的作用。

    3. why not all blink?

    其實“BCP + MetaQ + igps”的流程可以被“Blink + SLS”取代,那為什麼不都統一使用Blink呢。

    一方面,目前點擊的校驗由於其流量相對較小的因素,我們目前是直接在BCP里編寫的校驗代碼,不需要走發布流程,比較快捷。而且BCP擁有如“延遲校驗”、“限流控制”等個性化的功能。另一方面,從我們目前使用Blink的體驗來看,實時的處理引擎尚有一些不穩定的因素,尤其是會有不穩定的網絡抖動(可能是數據源和Blink workder跨機房導致)。

    4. SP請求的key值如何拆分?

    在做SP請求串的實時key值監控的時候,遇到了一個小難題:SP的請求串中參數key是動態的,並不是每個key都會在每個串中出現,而且不同的請求串key出現的順序是不一樣的。如何切分使其滿足Xflush的“列值分組”格式要求。

    實現方式是,對每個sp請求串,使用Blink的udtf(自定義表值函數)進行解析,得到每個串的所有key和其對應的value。然後輸出時,按照“validKey={key},validValue={value}”的格式對每個sp請求串拆分成多行輸出。然後通過Xflush可以按照validKey進行分組,並對行數進行統計。

    五、總結及後續規劃

    本文介紹了通過大數據的處理技術做到電商搜索廣告場景下數據端到端一致性問題的實時發現,並且通過“實時發現”結合“數據變更節點的主動校驗”,實現數據全流程的一致性校驗。

    後續的優化方向主要有兩方面:

    • 結合業務的使用場景,透出更豐富維度的實時數據。
    • 將該套技術體系“左移”到線下/預發測試階段,實現“功能、性能、效果”的一鍵式自動化測試,同時覆蓋從搜索應用到引擎的全鏈路。

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

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

  • 高質量App的架構設計與思考!

    高質量App的架構設計與思考!

    最近在做一功能不大、業務也不複雜的小眾App,以往做App是發現自己從來沒有考慮過一些架構方面的問題,只是按照自己以往的習慣去寫代碼,忽略了App的設計。本次分享主要包含一些開發App的小經驗和技巧,來一次App開發與設計的分享。

    先和分享下一下實體類的設計與組織形式

    實體類的組織

    在做App開發的時候有很多的實體類,項目越複雜實體類就會越多,經過我的一番思考大致這可以將實體分為以下幾大數:

    • 面向數據庫的
    • 服務端返回的數據實體
    • 用於渲染View的實體(使用Databinding)

    一般情況下實體類的操作會經過以下步驟:

    1. App請求服務器獲取數據
    2. 將數據存入數據庫(可選)
    3. 渲染頁面展示數據

    現在的實體的產生只用在請求服務器數據的時候才需要新建,後續的數據庫、頁面渲染其實是可以使用一套實體:

    先不說這樣做的行不行,首先三個地方使用同一實體就會引起字段歧義比如服務器數據有Id、本地數據也有Id,那兩個id字段就有衝突了不得不改字段名。

    另一種情況渲染和數據本身並不會一一對應,有時候後端數據給的是一個純数字而前端頁面显示的是字符串兩個都對應不上,強行放在一起會起來更多的問題。

    所為實體類的的正確組織形式應該是:相互隔離、互不干擾

    數據實體的在渲染之前都需要準備好,比如在ViewModel中將int型的數據轉換成文本型的數據然後再使用Databinding+頁面渲染實體來渲染頁面。

    優雅的處理網絡數據

    現在Android開發使用的網絡庫大部分都是Okhttp + Retrofit,使用Retrofit網絡交互變的非常簡單一個Service接口就能搞定一切,美茲茲~~,現在大部分後端返回的數據都會是以下形式:

    {
        "code":0,
        "data": {},
        "msg": ""
    }

    雖然不能涵蓋所有,但還是可以非常贊的數據、消息、成功與否啥都有!對於前面主要是關注data字段,其他msgcode等都屬於輔助字段。前端對應的實體對象應該是這樣的(假代碼):

    public class ApiResponse<T> {
        private int code;
        private T data;
        private String msg;
    }

    對應的Service那就得定義成這樣(使用了RxJava):

    public intface UserService {
        @GET("/xx/{id}")
        Single<ApiResponse<UserInfo> getUserInfoById(@Path("id") Long userId);
    }

    從接口中可以看出來,方法的返回值就包了幾層,如果要拿data字段需要經過:ApiResponse -> UserInfo,而且在拿之前還要判斷code字段:

    
    ...
    
    if(ApiResponse.code == 0){
        UserInfo info = ApiResponse.getData();
    }
    
    ...
    

    為了消除這些冗餘的代碼可以使用CallAdapter來使Service方法返回的數據直接就是實體類:

    public intface UserService {
        @GET("/xx/{id}")
        Single<UserInfo> getUserInfoById(@Path("id") Long userId);
    }

    CallAdapter的代碼就不貼了,可以自行查找。這樣做帶來的另外一個問題就是業務代碼如何判斷接口是否成功或失敗,前端必需友好的把錯誤提示給用戶而不是一直搞個Loading在那裡瞎轉~~。現階段最方便的的錯誤傳遞方式是使用Java異常,前端可以定義業務異常網絡異常

    public class BizException extends RuntimeException {
        ...
    }

    CallAdapter中檢查ApiResponse的返回值是否成功:

    
    if(!ApiResponse != 0){
        throw new BizExcepiton(ApiResponse);
    }
    

    如果後端返回業務異常那前端就對應拋出一個BizExcepiton,如果是http錯誤如:404、400那可以拋出HttpException。除了BizExcepitonHttpException外還可使用特定的異常比如後端返回密碼錯誤異常:

    public class InvalidPasswordException extends BizException {
        ...
    }

    如需特殊處理,也可以滿足要求。

    健壯的數據層

    現在很多應用都開發使用MVVM開發模式數據層都使用Repository來表示,面向數據驅動的開發模式,頁面變化都需要隨着數據變更而更新,數據發生變化然後頁面再做出響應。Repository的拆分要細一點,不建議簡單的弄個UserRepository包含登陸、註冊、更新密碼等等操作,設計Repository的一些想法:

    1. 面向接口編程
    2. 保持單一原則
    3. 功能邊界要清晰(如:登陸、註冊可以分開)
    4. 業務邏輯盡可能的少(複雜的業務考慮Presenter)

    一個判斷是否是好的設計的辦法可以這樣:一個登陸頁面從Activive/Fragment到ViewModel再到Repository,有沒有多餘的代碼。比如上面說的UserRepository包含登陸、註冊但是在一個登陸頁面就不需要有註冊功能,從登陸頁面上來看註冊的代碼就是多餘的(有些App登陸/註冊在一個頁面的~~)。

    一個包含登陸、註冊的UserRepository簡單圖:

    另外一點是盡量將repository使用到的一些東西集中管理,可引入一個基礎的repository:

    public class SimpleRepository {
        
        protected final  <T> T getService(Class<T> clz){
            return Services.getService(clz);
        }
    }
    

    做為SimpleRepository的子類,就不需要考慮從哪裡獲取service的問題。

    簡潔的UI層

    UI層面可以分為ViewModel和View(Activity/Fragment), View的職責應當只有二點:

    1. 展示業務數據
    2. 收集業務數據

    例如一些數據的組織、判斷都不應該出現在View中比如:

     if (Strings.isNullOrEmpty(phone)) {
           ...
            return;
     }
    
     if (Strings.isNullOrEmpty(pwd)) {
            ...
            return;
      }

    像上面這類的代碼都不應該出現在View中,而在放置在ViewModel裏面,View只收集用戶數據傳遞給ViewModel由它來進行數據校驗。再比如像這樣的if/else代碼也應該放置在ViewModel中:

     int age = 10;
     String desc = "";
     if(age < 18){
        desc = "青年";
     }else if(age < 29){
        desc = "中年";
     }

    如果數據的显示和數據的收集過多,建議使用Databinding來進行雙向綁定數據。再搭配LiveData使View作為觀察者實時監聽數據變化:

    registerViewModel.getRegistryResult().observe(this, new SimpleObserver<RegistryInfo>(this));

    一旦數據發生變化LiveData就會通知Observer更新,通過DataBinding更新各個頁面數據。

    再說ViewModel應該只包含一些簡單的判斷、檢查、打通數據的代碼,如果業務過於複雜可以考慮加Presetner,如果真的超級複雜那可以反思下這個複雜的邏輯應不應該放在前端,能不能放在後端呢?

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

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

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 理解MySQL數據庫事務-隔離性

    理解MySQL數據庫事務-隔離性

    Transaction事務是指一個邏輯單元,執行一系列操作的SQL語句。

    事務中一組的SQL語句,要麼全部執行,要麼全部回退。在Oracle數據庫中有個名字,叫做transaction ID

    在關係型數據庫中,事務必須ACID的特性。

    • 原子性,事務中的操作,要不全部執行,要不都不執行
    • 一致性,事務完成前後,數據的必須保持一致。
    • 隔離性,多個用戶併發訪問數據庫時,每一個用戶開啟的事務,相互隔離,不被其他事務的操作所干擾。
    • 持久性,事務一旦commit,它對數據庫的改變是持久性的。

    目前重點討論隔離性。數據庫一共有四個隔離級別

    • 未提交讀(RU,Read Uncommitted)。它能讀到一個事物的中間狀態,不符合業務中安全性的保證,違背 了ACID特性,存在臟讀的問題,基本不會用到,可以忽略

    • 提交讀(RC,Read Committed)。顧名思義,事務提交之後,那麼我們可以看到。這是一種最普遍的適用的事務級別。我們生產環境常用的使用級別。

    • 可重複讀(RR,Repeatable Read)。是目前被使用得最多的一種級別。其特點是有GAP鎖,目前還是默認級別,這個級別下會經常發生死鎖,低併發等問題。

    • 可串行化,這種實現方式,其實已經是不是多版本了,而是單版本的狀態,因為它所有的實現都是通過鎖來實現的。

    因此目前數據庫主流常用的是RCRR隔離級別。

    隔離性的實現方式,我們通常用Read View表示一個事務的可見性。

    RC級別,事務可見性比較高,它可以看到已提交的事務的所有修改。因此在提交讀(RC,Read Committed)隔離級別下,每一次select語句,都會獲取一次Read View,得到數據庫最新的事務提交狀態。因此對於數據庫,併發性能也最好。

    RR級別,則不是。它為了避免幻讀和不可重複讀。保證在一個事務內前後數據讀取的一致。其可見性視圖Read View只有在自己當前事務提交之後,才會更新。

    那如何保證數據的一致性?其核心是通過redo logundo log來保證的。

    而在數據庫中,為了實現這種高併發訪問,就需要對數據庫進行多版本控制,通過事務的可見性來保證事務看到自己想看到的那個數據版本(或者是最新的Read View亦或者是老的Read View)。這種技術叫做MVCC

    多版本是如何實現的?通過undo日誌來保證。每一次數據庫的修改,undo日誌會存儲之前的修改記錄值。如果事務未提交,會回滾至老版本的數據。其MVCC的核心原理,以後詳談

    舉例論證:

    ##  開啟事務
    MariaDB [scott]> begin;                   
    Query OK, 0 rows affected (0.000 sec)
    
    ##查看當前的數據
    MariaDB [scott]>  select * from dept;
    +--------+------------+----------+
    | deptno | dname      | loc      |
    +--------+------------+----------+
    |     10 | ACCOUNTING | beijing  |
    |     20 | RESEARCH   | DALLAS   |
    |     30 | SALES      | CHICAGO  |
    |     40 | OPERATIONS | beijing  |
    |     50 | security   | beijing  |
    |     60 | security   | nanchang |
    +--------+------------+----------+
    6 rows in set (0.001 sec)
    
    ##更新數據
    MariaDB [scott]> update dept set loc ='beijing' where deptno = 20;
    Query OK, 1 row affected (0.001 sec)
    
    ## 其行記錄| 20 | RESEARCH | DALLAS |已經被放置在undo日誌中,目前最新的記錄被改為'beijing':
    MariaDB [scott]> select * from dept;
    +--------+------------+----------+
    | deptno | dname      | loc      |
    +--------+------------+----------+
    |     10 | ACCOUNTING | beijing  |
    |     20 | RESEARCH   | beijing  |
    |     30 | SALES      | CHICAGO  |
    |     40 | OPERATIONS | beijing  |
    |     50 | security   | beijing  |
    |     60 | security   | nanchang |
    +--------+------------+----------+
    
    ##事務不提交,回滾。數據回滾至老版本的數據。
    MariaDB [scott]> rollback;
    Query OK, 0 rows affected (0.004 sec)
    
    MariaDB [scott]> select * from dept;
    +--------+------------+----------+
    | deptno | dname      | loc      |
    +--------+------------+----------+
    |     10 | ACCOUNTING | beijing  |
    |     20 | RESEARCH   | DALLAS   |
    |     30 | SALES      | CHICAGO  |
    |     40 | OPERATIONS | beijing  |
    |     50 | security   | beijing  |
    |     60 | security   | nanchang |
    +--------+------------+----------+
    6 rows in set (0.000 sec)

    因為MVCC,讓數據庫有了很強的併發能力。隨着數據庫併發事務處理能力大大增強,從而提高了數據庫系統的事務吞吐量,可以支持更多的用戶併發訪問。但併發訪問,會出現帶來一系列問題。如下:

    數據庫併發帶來的問題 概述解釋
    臟讀(Dirty Reads) 當一個事務A正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務B也訪問這同一個數據,如不控制,事務B會讀取這些”臟”數據,並可能做進一步的處理。這種現象被稱為”臟讀”(Dirty Reads)
    不可重複讀(Non-Repeatable Reads) 指在一個事務A內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務B也訪問該同一數據。那麼,在事務A的兩次讀數據之間,由於第二個事務B的修改,那麼第一個事務兩次讀到的的數據可能是不一樣的 。出現了”不可重複讀”(Non-Repeatable Reads)的現象
    幻讀(Phantom Reads) 指在一個事務A內,按相同的查詢條件重新檢索以前檢索過的數據,同時發現有其他事務插入了數據,其插入的數據滿足事務A的查詢條件。因此查詢出了新的數據,這種現象就稱為”幻讀”(Phantom Reads)

    隔離級別和上述現象之間的聯繫。

    隔離級別有:未提交讀(RU,Read Uncommitted),提交讀(RC,Read Committed),可重複讀(RR,Repeatable Read),可串行化(Serializable)

    隔離級別 臟讀 不可重複讀 幻讀
    未提交讀(RU,Read Uncommitted) 可能 可能 可能
    提交讀(RC,Read Committed) 不可能 可能 可能
    可重複讀(RR,Repeatable Read) 不可能 不可能 可能
    (間隙鎖解決)
    可串行化(Serializable) 不可能 不可能 不可能

    實驗環節

    舉例在隔離級別RRRC下,說明“不可重複讀”問題。

    MySQL的默認級別是Repeatable Read,如下:

    MariaDB [(none)]> select @@global.tx_isolation;
    +-----------------------+
    | @@global.tx_isolation |
    +-----------------------+
    | REPEATABLE-READ       |
    +-----------------------+
    1 row in set (0.000 sec)

    這裏修改當前會話級別為Read Committed

    MariaDB [scott]> set session transaction isolation level read committed;
    Query OK, 0 rows affected (0.001 sec)
    
    MariaDB [scott]> select @@tx_isolation;
    +----------------+
    | @@tx_isolation |
    +----------------+
    | READ-COMMITTED |
    +----------------+
    1 row in set (0.000 sec)

    在隔離級別已提交讀(RC,Read Committed)下,出現了不可重複讀的現象。在事務A中可以讀取事務B中的數據。

    在隔離級別可重複讀(RR,Repeatable Read),不會出現不可重複讀現象,舉例如下:

    舉例說明“幻讀”的現象。

    行鎖可以防止不同事務版本的數據在修改(update)提交時造成數據衝突的問題。但是插入數據如何避免呢?

    在RC隔離級別下,其他事務的插入數據,會出現幻讀(Phantom Reads)的現象。

    而在RR隔離級別下,會通過Gap鎖,鎖住其他事務的insert操作,避免”幻讀”的發生。

    因此,在MySQL事務中,鎖的實現方式與隔離級別有關,如上述實驗所示。在RR隔離級別下,MySQL為了解決幻讀的問題,已犧牲并行度為代價,通過Gap鎖來防止數據的寫入。這種鎖,并行度差,衝突多。容易引發死鎖。

    目前流行的Row模式可以避免很多衝突和死鎖問題,因此建議數據庫使用ROW+RC(Read Committed)模式隔離級別,很大程度上提高數據庫的讀寫并行度,提高數據庫的性能。

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

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

  • 【併發編程】Java中的原子操作

    【併發編程】Java中的原子操作

    什麼是原子操作

    原子操作是指一個或者多個不可再分割的操作。這些操作的執行順序不能被打亂,這些步驟也不可以被切割而只執行其中的一部分(不可中斷性)。舉個列子:

    //就是一個原子操作
    int i = 1;
    
    //非原子操作,i++是一個多步操作,而且是可以被中斷的。
    //i++可以被分割成3步,第一步讀取i的值,第二步計算i+1;第三部將最終值賦值給i
    i++;

    Java中的原子操作

    在Java中,我們可以通過同步鎖或者CAS操作來實現原子操作。

    CAS操作

    CAS是Compare and swap的簡稱,這個操作是硬件級別的操作,在硬件層面保證了操作的原子性。CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什麼都不做。Java中的sun.misc.Unsafe類提供了compareAndSwapIntcompareAndSwapLong等幾個方法實現CAS。

    另外,在jdk的atomic包下面提供了很多基於CAS實現的原子操作類,見下圖:

    下面我們就使用其中的AtomicInteger來看看怎麼使用這些原子操作類。

    package com.csx.demo.spring.boot.concurrent.atomic;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class AtomicIntegerDemo {
    
        private static int THREAD_COUNT = 100;
    
        public static void main(String[] args) throws InterruptedException {
    
    
            NormalCounter normalCounter = new NormalCounter("normalCounter",0);
            SafeCounter safeCounter = new SafeCounter("safeCounter",0);
            List<Thread> threadList = new ArrayList<>();
    
            for (int i = 0; i < THREAD_COUNT ; i++) {
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j < 10000; j++) {
                            normalCounter.add(1);
                            safeCounter.add(1);
                        }
                    }
                });
                threadList.add(thread);
            }
    
            for (Thread thread : threadList) {
                thread.start();
            }
            for (Thread thread : threadList) {
                thread.join();
            }
            System.out.println("normalCounter:"+normalCounter.getCount());
            System.out.println("safeCounter:"+safeCounter.getCount());
        }
    
    
        public static class NormalCounter{
            private String name;
            private Integer count;
    
            public NormalCounter(String name, Integer count) {
                this.name = name;
                this.count = count;
            }
    
            public void add(int delta){
                this.count = count+delta;
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public Integer getCount() {
                return count;
            }
    
            public void setCount(Integer count) {
                this.count = count;
            }
        }
    
        public static class SafeCounter{
            private String name;
            private AtomicInteger count;
    
            public SafeCounter(String name, Integer count) {
                this.name = name;
                this.count = new AtomicInteger(count);
            }
    
            public void add(int delta){
                count.addAndGet(delta);
            }
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public int getCount() {
                return count.get();
            }
    
            public void setCount(Integer count) {
                this.count.set(count);
            }
        }
    
    }

    上面的代碼中,我們分別創建了一個普通的計數器和一個原子操作的計數器(使用AtomicInteger進行計數)。然後創建了100個線程,每個線程進行10000次計數。理論上線程執行完之後,計數器的值都是1000000,但是結果如下:

    normalCounter:496527
    safeCounter:1000000

    每次執行,普通計數器的值都是不一樣的,而使用AtomicInteger進行計數的計數器都是1000000。

    CAS操作存在的問題

    • ABA問題:因為CAS需要在操作值的時候檢查下值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。ABA問題的解決思路就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那麼A-B-A 就會變成1A-2B-3A。

    從Java1.5開始JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法作用是首先檢查當前引用是否等於預期引用,並且當前標誌是否等於預期標誌,如果全部相等,則以原子方式將該引用和該標誌的值設置為給定的更新值。

    • 循環時間長開銷大:自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷。如果JVM能支持處理器提供的pause指令那麼效率會有一定的提升,pause指令有兩個作用,第一它可以延遲流水線執行指令(de-pipeline),使CPU不會消耗過多的執行資源,延遲的時間取決於具體實現的版本,在一些處理器上延遲時間是零。第二它可以避免在退出循環的時候因內存順序衝突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush),從而提高CPU的執行效率。

    • 只能保證一個共享變量的原子操作:當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖,或者有一個取巧的辦法,就是把多個共享變量合併成一個共享變量來操作。比如有兩個共享變量i=2,j=a,合併一下ij=2a,然後用CAS來操作ij。從Java1.5開始JDK提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個變量放在一個對象里來進行CAS操作。

    使用鎖來保證原子操作

    還是以上面的列子為列,普通的計數器我們只需要在計數方法上加鎖就行了:

    public synchronized void  add(int delta){
      this.count = count+delta;
    }

    執行結果如下:

    normalCounter:1000000
    safeCounter:1000000

    兩個計數器都能拿到正確的結果

    CPU是怎麼實現原子操作的

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

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

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 正則表達式 第五篇:C# 正則表達式

    正則表達式 第五篇:C# 正則表達式

    本文整理C#正則表達式的元字符,正則表達式是由字符構成的表達式,每個字符代表一個規則,表達式中的字符分為兩種類型:普通字符和元字符。普通字符是指字面含義不變的字符,按照完全匹配的方式匹配文本,而元字符具有特殊的含義,代表一類字符。

    把文本看作是字符流,每個字符放在一個位置上,例如,正則表達式 “Room\d\d\d”,前面四個字符Room是普通字符,後面的字符\是轉義字符,和後面的字符d組成一個元字符\d,表示該位置上有任意一個数字。

    用正則表達式的語言來描述是:正則表達式 “Room\d\d\d”共捕獲7個字符,表示“以Room開頭、以三個数字結尾”的一類字符串,我們把這一類字符串稱作一個模式(Pattern),也稱作是一個正則。

    一,轉義字符

    轉義字符是\,把普通字符轉義為具有特殊含義的元字符,常用的轉義字符有:

    • \t:水平製表符
    • \v:垂直製表符
    • \r:回車
    • \n:換行
    • \\:表示字符 \,也就說,把轉義字符 \ 轉義為普通的字符 \
    • \”:表示字符 “,在C#中,雙引號用於定義字符串,字符串包含的雙引號用 \” 來表示

    二,字符類

    在進行正則匹配時,把輸入文本看作是有順序的字符流,字符類元字符匹配的對象是字符,並會捕獲字符。所謂捕獲字符是指,一個元字符捕獲的字符,不會被其他元字符匹配,後續的元字符只能從剩下的文本中重新匹配。

    常用的字符類元字符:

    • [ char_group]:匹配字符組中的任意一個字符
    • [^char_group]:匹配除字符組之外的任意一個字符
    • [first-last]:匹配從first到last的字符範圍中的任意一個字符,字符範圍包括first和last。
    • .   :通配符,匹配除\n之外的任意一個字符
    • \w:匹配任意一個單詞(word)字符,單詞字符通常是指A-Z、a-z和0-9
    • \W:匹配任意一個非單詞字符,是指除A-Z、a-z和0-9之外的字符
    • \s:匹配任意一個空白字符
    • \S:匹配任意一個非空白字符
    • \d:匹配任意一個数字字符
    • \D:匹配任意一個非数字字符

    注意,轉義字符也屬於字符類元字符,在進行正則匹配時,也會捕獲字符。

    三,定位符

    定位符匹配(或捕獲)的對象是位置,它根據字符的位置來判斷模式匹配是否成功,定位符不會捕獲字符,是零寬的(寬度為0),常用的定位符有:

    • ^:默認情況下,匹配字符串的開始位置;在多行模式下,匹配每行的開始位置;
    • $:默認情況下,匹配字符串的結束位置,或 字符串結尾的\n之前的位置;在多行模式下,匹配每行結束之前的位置,或者每行結尾的\n之前的位置。
    • \A:匹配字符串的開始位置;
    • \Z:匹配字符串的結束位置,或 字符串結尾的\n之前的位置;
    • \z:匹配字符串的結束位置;
    • \G:匹配上一個匹配結束的位置;
    • \b:匹配一個單詞的開始或結束的位置;
    • \B:匹配一個單詞的中間位置;

     

    四,量詞、貪婪和懶惰

    量詞是指限定前面的一個正則出現的次數,量詞分為兩種模式:貪婪模式和懶惰模式,貪婪模式是指匹配盡可能多的字符,而懶惰模式是指匹配盡可能少的字符。默認情況下,量詞處於貪婪模式,在量詞的後面加上?來啟用懶惰模式。

    • *:出現0次或多次
    • +:出現1次或多次
    • ?:出現0次或1次
    • {n}:出現n次
    • {n,}:出現至少n次
    • {n,m}:出現n到m次

    注意,出現多次是指前面的元字符出現多次,例如,\d{2} 等價於 \d\d,只是出現兩個数字,並不要求兩個数字是相同的。要表示相同的兩個数字,必須使用分組來實現。

    五,分組和捕獲字符

    ()  括號不僅確定表達式的範圍,還創建分組,()內的表達式就是一個分組,引用分組表示兩個分組匹配的文本是完全相同的。定義一個分組的基本語法:

    (pattern)

    該類型的分組會捕獲字符,所謂捕獲字符是指:一個元字符捕獲的字符,不會被其他元字符匹配,後續的元字符只能從剩下的文本中重新匹配。

    1,分組編號和命名

    默認情況下,每個分組自動分配一個組號,規則是:從左向右,按分組左括號的出現順序進行編號,第一個分組的組號為1,第二個為2,以此類推。也可以為分組指定名稱,該分組稱作命名分組,命名分組也會被自動編號,編號從1開始,逐個加1,為分組指定名稱的語法是:

    (?< name > pattern)

    通常來說,分組分為命名分組和編號分組,引用分組的方式有:

    • 通過分組名稱來引用分組:\k<name>
    • 通過分組編號來引用分組:\number

    注意,分組只能後向引用,也就是說,從正則表達式文本的左邊開始,分組必須先定義,然後才能在定義之後面引用。

    在正則表達式里引用分組的語法為“\number”,比如“\1”代表與分組1 匹配的子串,“\2”代表與分組2 匹配的字串,以此類推。

    例如,對於 “<(.*?)>.*?</\1>” 可以匹配 <h2>valid</h2>,在引用分組時,分組對應的文本是完全相同的。

    2,分組構造器

    分組構造方法如下:

    • (pattern):捕獲匹配的子表達式,併為分組分配一個組號
    • (?< name > pattern):把匹配的子表達式捕獲到命名的分組中
    • (?:pattern):非捕獲的分組,並未分組分配一個組號
    • (?> pattern):貪婪分組

    3,貪婪分組

    貪婪分組也稱作非回溯分組,該分組禁用了回溯,正則表達式引擎將盡可能多地匹配輸入文本中的字符。如果無法進行進一步的匹配,則不會回溯嘗試進行其他模式匹配。

    (?> pattern )

    4,二選一

    | 的意思是或,匹配兩者中的任意一個,注意,|把左右兩邊的表達式分為兩部分。

    pattern1 | pattern2

    六,零寬斷言

    零寬是指寬度為0,匹配的是位置,所以匹配的子串不會出現在匹配結果中,而斷言是指判斷的結果,只有斷言為真,才算匹配成功。

    對於定位符,可以匹配一句話的開始、結束(^ $)或者匹配一個單詞的開始、結束(\b),這些元字符只匹配一個位置,指定這個位置滿足一定的條件,而不是匹配某些字符,因此,它們被成為 零寬斷言。所謂零寬,指的是它們不與任何字符相匹配,而匹配一個位置;所謂斷言,指的是一個判斷,正則表達式中只有當斷言為真時才會繼續進行匹配。零寬斷言可以精確的匹配一個位置,而不僅僅是簡單的指定句子或者單詞。

    正則表達式把文本看作從左向右的字符流,向右叫做後向(Look behind),向左叫做前向(Look ahead)。對於正則表達式,只有當匹配到指定的模式(Pattern)時,斷言為True,叫做肯定式,把不匹配模式為True,叫做否定式。

    按照匹配的方向和匹配的定性,把零寬斷言分為四種類型:

    • (?= pattern):前向、肯定斷言
    • (?! pattern):前向、否定斷言
    • (?<= pattern):後向、肯定斷言
    • (?<! pattern):後向、否定斷言

     1,前向肯定斷言

    前向肯定斷言定義一個模式必須存在於文本的末尾(或右側),但是該模式匹配的子串不會出現在匹配的結果中,前向斷言通常出現在正則表達式的右側,表示文本的右側必須滿足特定的模式:

     (?= subexpression )

    使用前向肯定斷言可以定一個模糊匹配,後綴必須包含特定的字符:

    \b\w+(?=\sis\b)

    對正則表達式進行分析:

    • \b:表示單詞的邊界
    •  \w+:表示單詞至少出現一次
    • (?=\sis\b):前向肯定斷言,\s 表示一個空白字符, is 是普通字符,完全匹配,\b 是單詞的邊界。

    從分析中,可以得出,匹配該正則表達式的文本中必須包含 is 單詞,is是一個單獨的單詞,不是某一個單詞的一個部分。舉個例子

    Sunday is a weekend day 匹配該正則,匹配的值是Sunday,而The island has beautiful birds 不匹配該正則。

    2,後向肯定斷言

    後向肯定斷言定義一個模式必須存在於文本的開始(或左側),但是該模式匹配的子串不會出現在匹配的結果中,後向斷言通常出現在正則表達式的左側,表示文本的左側必須滿足特定的模式:

    (?<= subexpression )

    使用後向肯定斷言可以定一個模糊匹配,前綴必須包含特定的字符:

    (?<=\b20)\d{2}\b

    對正則表達式進行分析:

    • (?<=\b20):後向斷言,\b表示單詞的開始,20是普通字符
    • \d{2}:表示兩個数字,数字不要求相同
    • \b:單詞的邊界

    該正則表達式匹配的文本具備的模式是:文本以20開頭、以兩個数字結尾。

    七,用正則從格式化的文本中扣值

    有如下的JSON格式的文本,從文本中扣出字段(CustomerId、CustomerName、CustomerIdSource和CustomerType)的值:

    {"CustomerDetails":"[{\"CustomerId\":\"57512f19\",\"CustomerName\":\"cust xyz\",\"CustomerIdSource\":\"AadTenantId\",\"CustomerType\":\"Enterprise\"}]"}

    注意,該文本轉換為C#中的字符時,需要對雙引號和轉義字符進行轉義。由於這四個字段提取規則相同,可以寫一個通用的模式來提取:

    public static string GetNestedItem(string txt, string key)
    {
        string pat = string.Format("(?<=\\\\\"{0}\\\\\":\\\\\").*?(?=\\\\\")", key);
        return Regex.Match(txt, pat, RegexOptions.IgnoreCase).Value;
    }

    正則表達式得解析:

    • (?<=\\\\\”{0}\\\\\”:\\\\\”):後向斷言,用於匹配字段名、雙引號和冒號
    • .*?:懶惰模式,匹配盡可能少的文本
    • (?=\\\\\”):前向斷言,用於匹配字段值得雙引號

     

     

    參考文檔:

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

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

  • 微服務框架 – Jimu(積木) 升級 1.0.0 支持 .Net Core 3.0

    微服務框架 – Jimu(積木) 升級 1.0.0 支持 .Net Core 3.0

    如果不知道 Jimu(積木) 是啥,請移步
    這次升級除了支持 .Net Core 3.0 還新增部分功能,如 REST, 鏈路跟蹤等,以下為詳細;

    一、功能列表

    功能 說明 Jimu 1.0.0 Jimu 0.6.0
    平台 .Net Core 2.1
    .Net Core 3.0
    服務註冊與發現 consul
    網關 Asp.Net Core Web
    RPC DotNetty
    鑒權 JWT
    負載均衡 輪訓
    容錯策略 重試
    容器 docker
    路由配置 Attribute註解
    日誌記錄 log4net
    nlog
    文檔 swagger
    鏈路跟蹤 skywalking
    REST Attribute註解
    健康監測 心跳
    文件上存下載 多文件上存,單文件下載
    跳轉 在服務端跳轉到指定url
    ORM Dapper
    DDD MiniDDD

    二、建議用積木結合 docker 搭建分佈式架構

    三、swagger

    四、skywalking

    拓撲圖: user -> jimu_apigateway -> jimu_order -> jimu_user

    Trace 跟蹤

    五、網關

    服務器

    微服務

    微服務詳細

    六、源碼

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

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

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 【面經系列】一線互聯網大廠前端面試技巧深入淺出總結

    【面經系列】一線互聯網大廠前端面試技巧深入淺出總結

    一二面(基礎面)

    1. 一面基礎面

    1.1 面試準備

    1.1.1 個人簡歷

    • 基本信息:姓名-年齡-手機-郵箱-籍貫
    • 工作經歷:時間-公司-崗位-職責-技術棧-業績(哪些成就)
    • 學歷: 博士 > 碩士 > 本科 > 大專
    • 工作經歷:時間-公司-崗位-職責-技術棧-業績
    • 開源項目:GitHub和說明

    1.2.2 自我陳述

    1.2.2.1 把我面試的溝通方向(別把自己帶到坑裡面)

    答:我平時喜歡研究一些網站,並對一些技術的原理和好玩的點感興趣,我自己也喜歡思考,也喜歡嘗試探索有沒有更好的方式和實現。(有所收留,不要全部說出來,稍微留一點懸念留作面試官來提問)

    1.2.2.2 豁達、自信的適度發揮

    答:適當自信,向自己擅長的方向上面來引路;要讓面試官來欣賞我,而不是來鄙視他。

    1.2.2.3 自如談興趣

    (豁達自信,適當收住),巧妙演示實例,適時討論疑問(不知道的問題請求指導一下,如何去解決,不要說不知道,或者不了解)

    1.2.2.4 節奏要適宜

    切忌小聰明(盡量把問題的所有實現方法都寫出來,表現出來的是熟練)

    1.2 面試實戰

    [!NOTE]
    > 1. 方向要對,過程要細(性能優化,過程詳細)
    > 2. 膽子要大、心態要和(算法題認真思考,認真使勁想;敢於承擔責任,不要輕易放棄)

    2. CSS相關

    2.1 頁面布局

    2.1.1 如何實現垂直居中布局呢?

    1.已知寬高

    /*v1*/
    .container {
        position: absolute;
        left: 50%;
        top: 50%;
        marigin-left: -width / 2;
        marigin-top: -width / 2;
    }
    
    /*v2*/
    .container {
        position: absolute;
        top: calc(50% - 5em);
        left: calc(50% - 9em);
    }
    

    2.未知寬高

    /*v1*/
    .container {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    
    /*v2:flex+ auto*/
    .wrapper {
        dislay: flex;
    }
    .content {
        margin: auto;
    }
    
    /*v3. 父元素居中*/
    .wrapper {
        display: flex;
        /* 盒子橫軸的對齊方式 */
        justify-content: center;
        /* 盒子縱軸的對齊方式 */
        align-items: center;
    }
    
    /*v4.body內部居中*/
    .content {
         /* 1vh = 1% * 視口高度 */
          margin: 50vh auto;
          transform: translateY(-50%);
    }

    2.1.2 如何實現水平居中布局呢?

    1. 如果需要居中的元素為常規流中 inline / inline-block 元素,為父元素設置 text-align: center;
    2. 父元素上設置 text-align: center; 居中元素上margin 為 auto。
    3. 如果元素positon: absolute; 那麼
      • 0)設置父元素postion: relative
      • 1)為元素設置寬度,
      • 2)偏移量設置為 50%,
      • 3)偏移方向外邊距設置為元素寬度一半乘以-1

    2.1.3 如何實現三欄布局呢?

    1. left和right寫在center前面,並且分別左右浮動;
    2. 左右區域分別postion:absolute,固定到左右兩邊;中間的這個div因為是塊級元素,所以在水平方向上按照他的包容塊自動撐開。
    3. 父元素display: table;並且寬度為100%; 每一個子元素display: table-cell; 左右兩側添加寬度,中間不加寬度
    4. 包裹這個3個塊的父元素display: flex; 中間的元素flex: 1;
    5. 網格布局
    /* 網格布局 */
    .wrapper {
        display: grid;
        width: 100%;
        grid-template-columns: 300px 1fr 300px;
    }

    2.2 知道CSS動畫的實現嗎?

    [!NOTE]
    知道transition 過渡動畫和animation 關鍵幀動畫區別和具體實現。

    • 1.CSS動畫實現輪播圖
    • 2.CSS動畫實現旋轉的硬幣
    • 3.CSS動畫實現鐘擺效果

    2.3 CSS盒子模型

    2.3.1 說一下CSS的盒子模型?標準模型和IE模型的區別?CSS如何設置這兩種模型?

    • 標準盒子模型:width = content
    • IE盒子模型:width = content + pading + border

    • box-sizing : content-box
    • box-sizing : border-box

    2.4 CSS樣式獲取

    2.4.1 JS如何設置獲取盒子模型對應的寬度和高度?(面試重點)

    • dom.style.width/height : 只能取到內聯樣式的的屬性信息(拿不到外部引入的CSS樣式信息的)
    • dom.currentStyle.width/height : 會拿到瀏覽器渲染之後的屬性信息(IE瀏覽器)
    • window.getComputedStyle(dom).width/height : Chrome/Firefox 兼容, Firefox可以通過document.defaultView.getComputedStyle(dom)的方式來獲取
    • dom.getBoundingClientRect().width/height : 可以獲取距離viewport位置的寬度和高度

    2.5 BFC

    2.5.1 根據盒子模型解釋邊距額重疊問題?邊距重疊問題的解決方案?

    • 父子元素
    • 兄弟元素
    • 其他 ————————–計算方式:以參數的最大值來進行計算

    解決方案:對父級元素創建BFC

    2.5.2 BFC原理

    [!NOTE]
    BFC: 塊級格式化上下文,IFC(內聯格式化上下文)

    1. 在BFC的垂直邊距上面會發生重疊
    2. BFC的區域不會與浮動元素的BOX重疊
    3. BFC在頁面上是一個獨立的渲染區域,外部的元素不會影響到我,同時也不會影響到外部的元素
    4. 計算BFC的高度的時候,浮動元素也會參与運算

    2.5.3 如何創建BFC?

    1. float值不是none
    2. position值不是static或者relative
    3. display值為table, table-cell, inline-box1.
    4. overflow : auto/hidden

    2.5.4 BFC的使用場景?(重點理解)

    1. 解決邊距的重疊問題
    <section id="margin">
            <style>
                #margin {
                    background-color: #4eff35;
                    overflow: hidden;
                }
                #margin>p {
                    /*上 左右 下*/
                    margin: 5px auto 25px;
                    background-color: #ff255f;
                }
            </style>
            <p>1</p>
            <!--把一個元素放在一個容器裏面,為這個容器創建BFC即可解決邊距重疊問題-->
            <div style="overflow: hidden">
                <p>2</p>
            </div>
    
            <p>3</p>
    </section>
    1. BFC 不與float部分重疊的解決
    <section id="layout">
          <style>
              #layout {
                  background-color: #48adff;
              }
              #layout .left {
                  float: left;
                  height: 300px;
                  width: 200px;
                  background-color: #ff4344;
              }
              #layout .right {
                  height: 400px;
                  background-color: #ff255f;
                  /*給右邊的這個盒子容器創建一個BFC, 這個容器裏面的內容就會沿着垂直方向延伸*/
                  overflow: auto;
                  /*overflow: auto;*/
                  /*display: table;*/
                  /*float: left;*/
                  /*position: fixed;*/
              }
          </style>
          <div class="left">
              LEFT
          </div>
          <div class="right">
              RIGHT
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
              <p>111</p>
          </div>
      </section>
    1. BFC子元素即使是float元素也要參与運算
    <section id="float">
          <style>
              /*一個盒子內部的內容如果是浮動的話,那麼這個盒子的內容實際上是不參与父容器高度計算的*/
              #float {
                  background-color: red;
                  /*overflow: hidden;*/
                  float: left;
              }
              #float .float {
                  float: left;
                  font-size: 30px;
              }
          </style>
          <div class="float">
              我是浮動的元素
          </div>
    </section>

    3. 事件相關

    3.1 DOM事件

    3.1.1 DOM事件的級別有哪些?

    [!NOTE]
    DOM級別一共可以分為四個級別:DOM0級、DOM1級、DOM2級和DOM3級。而DOM事件分為3個級別:DOM0級事件處理,DOM2級事件處理和DOM3級事件處理。

    1. DOM0 : element.onclick = function(e) {}
      DOM1 :該標準中未涉及到事件綁定的相關東西
    2. DOM2 : element.addEventListener(‘click’, function(e){}, false), 一個DOM元素可以添加多個事件
    3. DOM3 : element.addEventListener(‘keyup’, function(e){}, false),在DOM2標準基礎上面增加了新的事件類型:鼠標事件,鍵盤事件,焦點事件

    3.1.2 DOM事件模型有哪些?

    1. 事件捕獲:從外向內, window -> document -> body -> button
    2. 事件冒泡:從內向外,button -> body -> document -> window

    3.1.3 DOM事件流?

    瀏覽器為當前的頁面與用戶進行交互的過程中,點擊鼠標後事件如何傳入和響應的呢?

      1. 捕獲階段:從外部容器開始向內
      1. 目標階段:事件通過捕獲到達目標階段
      1. 冒泡階段:從目標元素再上傳到window對象

    3.1.4 什麼事件可以代理?什麼事件不可以代理呢?

    什麼樣的事件可以用事件委託,什麼樣的事件不可以用呢?

    [!NOTE]

    1. 通常支持事件冒泡(Event Bubbling)的事件類型為鼠標事件和鍵盤事件,例如:mouseover, mouseout, click, keydown, keypress。
    2. 接口事件(指的是那些不一定與用戶操作有關的事件)則通常不支持事件冒泡(Event Bubbling),例如:load, change, submit, focus, blur。

    很明顯:focus 和 blur 都屬於不支持冒泡的接口事件。既然都不支持冒泡,那又如何實現事件代理呢?

    3.1.5 IE和DOM事件流的區別?

    IE採用冒泡型事件 Netscape使用捕獲型事件 DOM使用先捕獲后冒泡型事件

    1. 冒泡型事件模型: button -> div -> body (IE瀏覽器本身只支持Bubbling不支持Capturing)
    2. 捕獲型事件模型: body -> div-> button (Netscape事件流,網景瀏覽器公司)
    3. DOM事件模型: body -> div -> button -> button -> div -> body (先捕獲后冒泡,除了IE以外的其他瀏覽器都支持標準的DOM事件處理模型)

    [!NOTE]

    • 規範和瀏覽器實現的差別?
      • DOM2級事件規範的捕獲階段,事件從文檔節點document開始傳播,現代瀏覽器大多數都是從window對象開始傳播事件的;
      • DOM2級事件規範捕獲階段不涉及事件目標,現代瀏覽器大多數都在這個階段包含事件目標。

    3.1.6 事件對象event的屬性方法的差別?

            IE                    DOM
    cancelBubble = true    stopPropagation()    // 停止冒泡
    returnValue = false    preventDefault()     // 阻止元素默認事件
    srcEelement            target               // 事件目標

    3.1.7 描述DOM事件捕獲的具體流程?

    window -> document -> HTML標籤 -> body -> … -> 目標元素

    [!NOTE]
    關鍵點: 注意根節點是window這個對象的

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
      <div id="container">
          <style>
              #container {
                  width: 200px;
                  height: 200px;
                  background-color: #ff255f;
              }
          </style>
      </div>
      <script>
          // 事件捕獲機制
          window.addEventListener('click', function(){
              console.log('window capture');
          }, true)
          document.addEventListener('click', function () {
              console.log('document capture');
          }, true)
          document.documentElement.addEventListener('click', function () {
              console.log('HTML capture');
          }, true)
          document.body.addEventListener('click', function () {
              console.log('body capture');
          }, true)
          document.getElementById('container').addEventListener('click', function () {
              console.log('container capture');
          }, true)
    
          // 事件冒泡機制
          window.addEventListener('click', function(){
              console.log('window capture');
          })
          document.addEventListener('click', function () {
              console.log('document capture');
          })
          document.documentElement.addEventListener('click', function () {
              console.log('HTML capture');
          })
          document.body.addEventListener('click', function () {
              console.log('body capture');
          })
          document.getElementById('container').addEventListener('click', function () {
              console.log('container capture');
          })
    
          // 輸出結果
          window capture  --> document capture --> HTML capture --> body capture --> container capture --> container capture -->  body capture --> HTML capture --> document capture --> window capture
      </script>
    </body>
    </html>

    3.1.8 如何拿到HTML這個標籤節點元素呢?(加分項)

      var html = document.documentElement;

    3.1.9 描述Event對象的常見應用?

    1. e.preventDefault() : 阻止默認事件(如阻止a標籤的默認跳轉行為)
    2. e.stopPropagation() : 阻止事件冒泡的行為
    3. *** e.stopImmediatePropagation() : 事件響應的優先級的應用場景,如果一個元素綁定了多個事件,但是又不想讓其他的事件執行的時候使用該方法【也會阻止冒泡】
    4. e.currentTarget : 當前所綁定的事件對象
      document.documentElement.onclick = function(e) {
        console.log(e.currentTarget, e.target);       // <html><body>...</body></html>()給綁定事件的那個元素, 當前被點擊的那個元素
      }

    [!NOTE]
    e.target : 當前被點擊的元素,父元素使用事件代理的方式來實現,可以直接使用該屬性獲取被點擊的那個元素

    3.2 如何自定義事件?(重點))

    3.2.1 如何給一個按鈕綁定一個自己定義的事件呢?

      // v1. 使用Event對象來自定義事件
      // 開始創建一個自己定義的事件對象
      var eve = new Event('customEvent');
      // 使用dom2事件處理的方式來給這個元素綁定一個事件
      var dom = document.documentElement;
      dom.addEventListener('customEvent', function(e) {
        console.log('customEvent called!');
      });
      // 下面的這句話可以在適合的場景中來觸發一個自己定義的事件對象
      setTimeout(function(){
        // 在1s之後觸發這個事件
        dom.dispatchEvent(eve);
      }, 1000)
    
    
      // v2. 使用CustomEvent來實現自定義事件
      var dom = document.documentElement;
      // 使用CustomEvent的方式可以在事件觸發的時候傳遞一個參數,然後通過e.detail 的方式來獲取這個參數信息
      var myClick = new CustomEvent('myClick', {detail : {name : 'zhangsan', age : 24}});
      dom.addEventListener('myClick', function(e){
        console.log(e.detail, e.target)
      })
      dom.dispatchEvent(myClick);
    

    4. HTTP協議

    4.1 HTTP協議的主要特點?

    • 簡單快速
    • 靈活
    • 無連接
    • 無狀態

    4.2 HTTP報文的組成部分?

    • 請求報文
      請求行:請求方法 資源地址 HTTP版本
      請求頭: key : value
      空行 :
      請求體 : name=zhangsan&age=18
    • 響應報文 : HTTP版本 狀態碼
      狀態行
      響應頭
      空行
      響應體

    4.3 HTTP方法?

    • GET : 獲取資源
    • POST : 傳輸資源
    • PUT :更新資源
    • DELETE : 刪除資源
    • HEAD :獲取報文首部
    • OPTIONS : 允許客戶端查看服務器的性能。

    4.4 POST和GET的區別?

    1. GET請求在瀏覽器回退的時候是無害的,而POST會再次提交請求
    2. GET請求產生的URL地址可以被收藏,而POST不可以
    3. GET請求會被瀏覽器主動緩存,而POST不會,除非主動設置
    4. GET請求只能進行URL編碼,而POST支持多種編碼方式
    5. GET請求參數會被完整第保留在瀏覽器的歷史記錄裏面,而POST參數不會被保留
    6. GET請求愛URL中傳送的參數的長度是有限的(2KB),而POST沒有限制
    7. 對參數的數據類型,GET值接受ASCII字符,而POST沒有限制
    8. POST比GET更安全,GET參數直接暴露在URL上,所以不能用來傳遞敏感信息
      9. GET參數通過URL傳遞,POST參數直接放在了Request body中

    4.5 HTTP狀態碼?

    4.5.1 狀態碼的第一位

    • 1xx :指示信息-表示請求已接收,繼續處理(重點)
    • 2xx :成功-表示請求已被成功接收
    • 3xx :重定向-要完成請求必須進行更進一步的操作
    • 4xx :客戶端錯誤-請求有語法錯誤或請求無法實現
    • 5xx :服務器錯誤-服務器未能實現合法的請求

    4.5.2 狀態碼詳解

    • 200 OK : 客戶端請求成功
    • 206 Partial Content : 客戶端發送了一個帶有Range頭的GET請求(Video標籤或者audio標籤在請求數據的時候)
    • 301 Moved Permanently : 請求的頁面已經轉移到了新的URL
    • 302 Found : 所請求的頁面已經臨時轉移到了新的URL
    • 304 Not Modified :客戶端有緩衝的文檔併發出了一個條件下的請求,原來緩衝的文檔還可以繼續使用
    • 400 Bad Request : 客戶端請求有語法錯誤,不被服務器所理解
    • 401 Unauthorized : 請求未經授權,這個狀態碼必須和WWW-Authenticate報頭域一起使用
    • 403 Forbidden:對被請求頁面的訪問被禁止
    • 404 Not Found : 請求資源不存在
    • 500 Internal Server Error :服務器發生不可預期的錯誤,原來緩衝的文檔還可以繼續使用
    • 503 Service Unavailable : 請求未完成,服務器臨時過載或宕機,一段時間后可能恢復正常

    4.6 什麼是持久連接?

    [!NOTE]
    HTTP協議採用‘請求-應答’模式, HTTP1.1版本才支持的,使用Keep-alive字段可以建立一個長連接,從而不需要每次請求都去建立一個新的連接。

    4.7 什麼是管線化?

    4.7.1 基本概念

    • 在使用持久連接(Keep-alive)的情況下,某個連接上的消息的傳遞類似於:請求1 –> 響應1 –> 請求2 –> 響應2 –> 請求3 –> 響應3
    • 管線化的過程: 請求1 –> 請求2 –> 請求3 –> 響應1 –> 響應2 –> 響應3

    4.7.2 管線化的特點(特點)

    1. 管線化機制通過持久連接完成,僅在HTTP1.1版本之後支持
    2. 只有GET和HEAD請求可以進行管線化,POST有所限制的
    3. 初次創建連接的時候不應該啟動管線機制,因為對方(服務器)不一定支持HTTP1.1版本的協議
    4. 管線化不會影響到響應到來的順序,HTTP響應返回的順序並未改變
    5. HTTP1.1 要求服務器支持管線化,但並不要求服務器也對響應進行管線化處理,只是要求對於管線化的請求不失敗即可
    6. 由於上面提到的服務器端問題,開啟管線化很可能並不會帶來大幅度的性能提升,而且很多服務器和代理程序對管線化的支持並不好,因此現代的瀏覽器如Chrome和Firefox默認並沒有開啟管線化支持

    5. 原型鏈

    5.1 創建對象的幾種方法?

    // 1. 使用字面量的方式來創建
    var o1 = {name : 'zhangsan'};
    var o11 = new Object({name : 'zhangsan'});
    
    // 2. 使用普通構造函數的方式來創建
    var M = function(){
        this.name = 'zhangsan';
    }
    var o2 = new M();
    
    // 3. Object.create方法
    var p = {name : 'zhangsan'};
    var o3 = Object.create(p);

    5.2 原型、構造函數、實例、原型鏈?

    構造函數:使用new運算符來聲明一個實例(任何函數都是可以通過構造函數來使用的)

    原型鏈:通過原型鏈可以找到上一級別的原型對象

    原型對象:多個實例公用的數據和屬性或者方法

    5.3 instanceof的原理?

    [!NOTE]
    instanceof 檢測一個對象A是不是另一個對象B的實例的原理是:查看對象B的prototype指向的對象是否在對象A的[[prototype]]鏈上。如果在,則返回true,如果不在則返回false。不過有一個特殊的情況,當對象B的prototype為null將會報錯(類似於空指針異常)。

    // 2. 使用普通構造函數的方式來創建
    var M = function(){
      this.name = 'zhangsan';
    }
    var o2 = new M();
    undefined
    o2.__proto__ == M.prototype
    true
    o2.__proto__ == M.prototype
    true
    o2.__proto__.constructor === Object
    false
    o2.__proto__.constructor === M
    true

    5.4 new運算符的原理?

    1. 一個新對象被創建。它繼承於foo.prototype
    2. 構造函數foo被執行。執行的時候,相應的傳參會被傳入,同時上下文(this)會被指定為這個新實例,new foo等同於 new foo(),只能用在不傳遞任何參數的情況
    3. 如果構造函數返回了一個“對象”,那麼這個對象會取代整個new出來的結果。如果構造函數沒有返回對象,那麼new 出來的結果為步驟1創建的對象
    // new 一個對象的過程
    var _new = function (fn) {
      // 1. 創建一個對象,這個對象要繼承fn這個構造函數的原型對象
      var o = Object.create(fn.prototype);
      // 2. 執行構造函數
      var k = fn.call(o, arguments);
      // 3. 看下執行的這個函數的運行效果是不是函數
      if (typeof k === 'object'){
          return k;
      }
      else
      {
          return o;
      }
    }

    6. 面向對象

    6.1 類與繼承:如何實現繼承,繼承的幾種實現方式

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <script>
      // 類的聲明
      function Animal1() {
          this.name = 'name';
      }
      // ES6 中的class的聲明
      class Animal2 {
          constructor(){
              this.name = 'name';
          }
      }
    
      console.log(new Animal1(), new Animal2());
      ///////////////////////////////////////////////////////////////////////////////////////////
    
    
      // 如何實現類的繼承呢???-----------本質:原型鏈
      // v1. 藉助構造函數實現繼承
      function Parent1() {
          this.name = 'parent1'
      }
      Parent1.prototype.sayHello = function () {
          console.log('hello');
      }
      function Child1() {
          // 執行父親的構造函數:
          // 1. 實現原理:將父級函數的this指向了這個子類的實例上面去了
          // 2. 缺點:父親的原型鏈上面的方法或者屬性不能被繼承;只能實現部分繼承
          Parent1.call(this);
          this.type = 'child1';
      }
      // 沒有參數的時候,可以直接new + 函數名稱
      console.log(res = new Child1);
    
    
    
    
      // v2. 藉助原型鏈實現繼承
      function Parent2() {
          this.name = 'parent2';
          this.data = [1, 2, 3];
      }
      Parent2.prototype.sayHello = function () {
          console.log('hello');
      }
      function Child2() {
          this.type = 'child2';
      }
      // prototype 就是為了讓這個對象的實例可以訪問到原型鏈上的內容
      Child2.prototype = new Parent2();
      // new Child2().__proto__ === Child2.prototype  // true
      // new Child2().__proto__.name                  // parent2
      // 原型鏈繼承的缺點:
      // 1. 原理:通過修改原型鏈來實現對象的繼承關係
      // 2. 缺點:修改第一個對象上面的屬性,會直接修改第二個對象屬性數據(引用類型)
      var c1 = new Child2();
      var c2 = new Child2();
      c1.data.push(100, 200, 300);
    
      // v3. 組合繼承
      function Parent3() {
          this.name = 'parent3';
          this.data = [1, 2, 3];
      }
      function Child3() {
          // 1. 借用構造函數繼承
          Parent3.call(this);
          this.type = 'child3';
      }
      // 2. 原型鏈繼承
      // child3的原型對象是Parent3的一個實例對象,但是這個實例對象中是沒有constructor這個屬性的,因此尋找屬性的時候回沿着這個實例對象的原型鏈繼續向上尋找new Parent3().prototype 這個原型對象的,
      // 最終在Parent3.prototype這個原型對象中找到了這個屬性,new一個對象找的實際上是{Parent3.prototype.constructor : Parent3}
      Child3.prototype = new Parent3();
      var c1 = new Child3();
      var c2 = new Child3();
      c1.data.push(100, 200, 300);
      // 組合繼承的特點:
      // 1. 原理:結合借用構造函數繼承和原型鏈繼承的優點,摒棄二者的缺點
      // 2. 缺點:父類構造函數在創建實例的時候總共執行了兩次(new Parent3(), new Child3())
    
    
      // v4. 組合繼承的優化1
      function Parent4() {
          this.name = 'parent4';
          this.data = [1, 2, 3];
      }
      function Child4() {
          // 1. 借用構造函數繼承
          Parent4.call(this);
          this.type = 'child4';
      }
      // 讓子類的構造函數的原型對象和父類構造函數的原型對象執向同一個對象(都是同一個對象)
      Child4.prototype = Parent4.prototype;
      // 測試
      var c1 = new Child4();
      var c2 = new Child4();
      console.log(c1 instanceof Child4, c1 instanceof Parent4);
      console.log(c1.constructor)         // Parent4? 如何實現:c1.constructor(c1.__proto__.constructor) === Child4 呢?
      // 缺點:
      // 1. 無法通過原型對象的constructor屬性來獲取對象的屬性對應的構造函數了(子類和父類公用的是一個contructor)
      // 2. obj instanceof Child4 === true; obj instanceof Parent4 === true
      // 3. obj.__proto__.constructor === Child4; obj.__proto__.constructor === Parent4  ???
    
      // v5. 組合繼承的優化2【完美寫法】
      function Parent5() {
          this.name = 'parent5';
          this.data = [1, 2, 3, 4, 5];
      }
      function Child5(){
          Parent5.call(this);
          this.type = 'child5';
      }
    
      // 通過創建中間對象的方式來把兩個對象區分開
      // var obj = new Object(); obj.__proto__ = Constructor.prototype;
      // 1. Object.create創建的對象obj, 這個obj的原型對象就是參數
      // 2. Child5的原型對象是Child5.prototype
      // 3. Child5.prototype = obj,obj這個對象相當於就是一个中間的橋樑關係
      Child5.prototype = Object.create(Parent5.prototype);
      // 當前的方式還是會按照原型鏈一級一級向上尋找的, 給Child5的原型對象上面綁定一個自己定義的constructor屬性
      Child5.prototype.constructor = Child5;
    
      // var s1 = new Child5()
    
      // 上面的代碼等價於
      var obj = Object.create(Parent5.prototype);     // obj.prototype = Parent5.prototype
      Child5.prototype = obj;
      Child5.prototype.constructor = Child5;
      // 1. 對象之間就是通過__proto__ 屬性向上尋找的
      // 2. 尋找規則: child5 ---> Child5.prototype ---> obj(Object.create(Parent5.prototype)) ---> Parent5.prototype
    
    
      // 技巧:不要讓面試官問太多題目:拖拉時間【擠牙膏】,把一個問題盡量吃透
      // 消化這一塊內容
    </script>
    </body>
    </html>

    [!WARNING]
    面試技巧

    1. 不要讓面試官問太多題目:拖拉時間【擠牙膏】,把一個問題盡量吃透
    2. 知識深度

    7. 通信

    7.1 什麼是同源策略個限制?

    [!NOTE]
    同源策略限制是從一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。(一個源的文檔或腳本是沒有權利直接操作另外一個源的文檔或腳本的)

    1. Cookie, LocalStorage和IndexDB無法讀取
    2. DOM無法獲得;(document.body是無法獲取的)
    3. Ajax請求不能發送

    7.2 前後端如何進行通信呢?

    1. Ajax(有同源策略限制);Fetch API則是XMLHttpRequest的最新替代技術, 它是W3C的正式標準
    2. WebSocket:支持跨域請求數據,沒有同源策略的限制
    3. CORS:新的協議通信標準;CORS則將導致跨域訪問的請求分為三種:Simple Request,Preflighted Request以及Requests with Credential;cors相對於jsonp而言的好處就是支持所有的請求方式,不止是get請求,還支持post,put請求等等,而它的缺點就很明顯,無法兼容所有的瀏覽器,對於要兼容到老式瀏覽器而言,還是使用jsonp好點

    7.3 如何創建Ajax呢?

    1. XMLHttpRequest對象的工作流程
    2. 瀏覽器的兼容性處理【重點】
    3. 事件的觸發條件
    4. 事件的觸發順序
      function ajax(params){
        // 1. 創建對象,考慮兼容性【重點】
        var xhr = XMLHTTPRequest ? new XMLHTTPRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');      // *** 兼容性問題必須考慮
        // 2. 打開連接
        var type = params.type || 'GET',
            url = params.url || '',
            data = params.data || {},
            success = params.success,
            error = params.error,
            dataArr = [];
        for (var k in data) {
          dataArr.push(k + '=' + data[k]);
        }
        //帶上Cookie
        xhr.withCredentials = true;
        if (type.toUpperCase() === 'GET') {
          // get
          url += '?' + dataArr.join('&');
          // 問號結尾的話,直接替換為空字符串
          xhr.open(type, url.replace(/\?$/g, ''), true);
          // GET 請求的話,是不需要再send方法中帶上參數的
          xhr.send();
        }
        else {
          // POST
          xhr.open(type, url, true);
          xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
          // POST 請求需要把數據放在send方法裏面, data = name=zhangsna&age=18&sex=male
          xhr.send(dataArr.join('&'));
        }
        // 開始監聽變化
        xhr.onreadystatechange = function(){
          // 這裏需要考慮強緩存和協商緩存的話直接處理,206是媒體資源的創建方式
          if (xhr.readyState === 4 && xhr.status === 200 || xhr.status === 304) {
              var res;
              if (success instanceof Function) {
                res = xhr.responseText;
                if (typeof res === 'string') {
                  res = JSON.parse(res);
                  // 開始執行成功的回調函數
                  success.call(xhr, res);
                }
              } else {
                if (error instanceof Function) {
                  // 失敗的話直接返回這個responseText中的內容信息
                  error.call(xhr, res);
                }
              }
          }
        }
      }

    7.4 跨域通信的幾種方式?

    7.4.1 JSONP

      function jsonp(url, onsuccess, onerror, charset){
        // 1. 全局註冊一個callback
        var callbackName = 'callback' + Math.random() * 100;
        window[callbackName] = function(){
          if (onsuccess && typeof onsuccess === 'Function') {
            onsuccess(arguments[0]);
          }
        }
        // 2. 動態創建一個script標籤
        var script = document.createElement('script');
        script.setAttribute('type', 'text/javascript');
        charset && script.setAttribute('charset', charset);
        script.setAttribute('src', url);
        script.async = true;
        // 3. 開始監聽處理的過程
        script.onload = script.onreadystatechange = function(){
          if (!script.readyState || /loaded|complete/.test(script.readyState)) {
            // 4. 成功之後移除這個事件
            script.onload = script.onreadystatechange = null;
            // 刪除這個script的DOM對象(head.removeChild(script), 這個DOM節點的父節點相當於是head標籤這個父節點)
            script.parentNode && script.parentNode.removeChild(script);
            // 刪除函數或變量
            window[callbackName] = null;
          }
        }
        script.onerror = function(){
          if (onerror && typeof onerror === 'Function') {
            onerror();
          }
        }
        // 5. 開始發送這個請求(把這個標籤放在頁面中的head標籤中即可)
        document.getElementsByTagName('head')[0].appendChild(script);
      }

    7.4.2 Hash

    hash 改變后頁面不會刷新的

    [!NOTE]
    使用場景:當前的頁面A通過iframe或者frame嵌入了跨域的頁面

      // 1. A頁面中的代碼如下
      var B = document.getElementsByTagName('iframe');
      B.src = B.src + '#' + JSON.stringfy(data);
      // 2. B中的偽代碼如下
      window.onhashchange = function(){
        var data = window.location.hash;    // 接受數據
        data = JSON.parse(data);
      }

    7.4.3 postMessage(HTML5中新增)

    [!NOTE]
    使用場景: 可以實現窗口A(A.com)向窗口B(B.com)發送信息

      // 1. 窗口B中的代碼如下
      var BWindow = window;
      BWindow.postMessage(JSON.stringfy(data), 'http://www.A.com');   
      // 2. 窗口A中代碼
      var AWindow = window;
      AWindow.addEventListener('message', function(e){
          console.log(e.origin);                  // http://www.B.com
          console.log(e.source);                  // BWindow
    
          e.source.postMessage('已成功收到消息');
    
          console.log(JSON.parse(e.data));        // data
      }, false)
      // 父窗口給子窗口發信息,需要用iframe的contentWindow屬性作為調用主體
      // 子窗口給父窗口發的信息需要使用window.top,多層iframe使用window.frameElement

    7.4.4 . WebSocket

    [!NOTE]
    不受同源策略影響,可以直接使用

      var ws = new window.WebSocket('ws://echo.websocket.org');
    
      // 打開連接
      ws.onopen = function(e){
        console.log('Connection open ……');
        ws.send('Hello WebSocket!');
      }
    
      // 接受消息
      ws.onmessage = function(e){
        console.log('Received Message : ', e.data);
      }
    
      // 關閉連接
      ws.onclose = function(e){
        console.log('Connection closed');
      }

    7.4.5 CORS

    支持跨域通信版本的Ajax,是一種新的標準(Origin頭)【ajax的一個變種,適用於任何】

      fetch('/get/name', {
        method : 'get'
      }).then(function(response){
        console.log(response);
      }).catch(function(err){
        // 出錯了;等價於then的第二個參數
      });
      // 原因:瀏覽器默認會攔截ajax請求,會根據頭中的origin消息進行判斷處理消息;Origin字段用來說明,本次請求來自哪個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否同意這次請求。JSONP只支持GET請求,CORS支持所有類型的HTTP請求。JSONP的優勢在於支持老式瀏覽器,以及可以向不支持CORS的網站請求數據。
    7.4.5.1 CORS請求的基本流程
    1. 對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在頭信息之中,增加一個Origin字段。
    2. Origin字段用來說明,本次請求來自哪個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否同意這次請求。
    3. 如果Origin指定的源,不在許可範圍內,服務器會返回一個正常的HTTP回應。瀏覽器發現,這個回應的頭信息沒有包含Access-Control-Allow-Origin字段(詳見下文),就知道出錯了,從而拋出一個錯誤,被XMLHttpRequest的onerror回調函數捕獲。
    4. 如果Origin指定的域名在許可範圍內,服務器返回的響應,會多出幾個頭信息字段。
      Access-Control-Allow-Origin: http://api.bob.com   // 必需的字段
      Access-Control-Allow-Credentials: true            // 可選字段: 是否允許發送cookie
      Access-Control-Expose-Headers: FooBar
      Content-Type: text/html; charset=utf-8
    1. 簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為”預檢”請求(preflight)。OPTIONS表示當前的這個請求是用來詢問的;服務器收到”預檢”請求以後,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以後,確認允許跨源請求,就可以做出回應。
    7.4.5.2 JSONP和CORS的區別?
    1. JSONP只支持GET請求,CORS支持所有類型的HTTP請求
    2. JSONP的優勢在於支持老式瀏覽器,以及可以向不支持CORS的網站請求數據。

    8. 安全

    8.1 CSRF

    8.1.1 基本概念和縮寫

    CSRF: 跨站請求偽造,Cross site request forgery

    8.1.2 CSRF 攻擊原理

    8.1.3 可以成功攻擊的條件?

    1. 目標網站存在CSRF漏洞的請求接口(一般為get請求)
    2. 目標用戶之前已經成功登錄過這個網站(留下了Cookie)

    8.1.4 如何防禦呢?

    1. Token驗證:訪問服務器接口的時候,會自動帶上這個token
    2. Referer驗證:驗證網站的頁面來源(只有我當前網站下的頁面才可以請求,對於來自其他網站的請求一律攔截)
    3. 隱藏令牌: 隱藏信息會放在header中(類似於Token)

    8.2 XSS

    8.2.1 基本概念和縮寫

    XSS: cross-site scripting, 跨站腳本攻擊

    8.2.2 XSS防禦

    攻擊原理: 注入JS腳本

    防禦措施: 讓JS代碼無法解析執行

    8.3 CSRF和XSS的區別呢?

    1. CSRF:網站本身存在漏洞的接口,依賴這些登錄過目標網站的用戶來實現信息的竊取;
    2. XSS:向頁面中注入JS執行,JS函數體內執行目標任務;

    [!NOTE]

    1. 一定要說出中文名稱,實現原理,防範措施都說出來
    2. 不要拖泥帶水,言簡意賅

    9. 算法

    [!NOTE]
    算法攻略:多刷題才是硬道理!!!

    二三面(知識深度面)

    10. 渲染機制

    10.1 什麼是DOCTYPE及作用?

    1. DTD(Document Type Definition):文檔類型定義,是一系列的語法規則,用來定義XML或者(X)HTML的文件類型。瀏覽器會使用它來判斷文檔的類型,決定使用哪一種協議來解析,以及切換瀏覽器模式;
    2. DOCTYPE: 是用來聲明文檔類型和DTD規範的,一個主要的用途是文件的合法性驗證;如果文件代碼不合法,那麼瀏覽器解析的時候就會出現一些出錯
    3. 總結:Doctype就是通知瀏覽器當前的文檔是屬於那種類型的,包含哪些DTD。
      <!--HTML5的寫法-->
      <DOCTYPE html>
      <!-- HTML 4.01  Strict
        1. 這個DTD 包含所有的HTML元素和屬性
        2. 但是不包含展示性的和棄用的元素(比如font)
      -->
      <DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd" >
      <!-- HTML 4.0.1 Transitional
        1. 這個DTD 包含所有的HTML元素和屬性
        2. 也包含展示性的和棄用性的元素(比如font)
      -->
      <DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" " http://www.w3.org/TR/html4/loose.dtd" >

    [!NOTE]
    在W3C標準出來之前,不同的瀏覽器對頁面渲染有不同的標準,產生了一定的差異。這種渲染方式叫做混雜模式。在W3C標準出來之後,瀏覽器對頁面的渲染有了統一的標準,這種渲染方式叫做標準模式。<!DOCTYPE>不存在或者形式不正確會導致HTML或XHTML文檔以混雜模式呈現,就是把如何渲染html頁面的權利交給了瀏覽器,有多少種瀏覽器就有多少種展示方式。因此要提高瀏覽器兼容性就必須重視<!DOCTYPE>

    10.2 嚴格模式和混雜模式

    [!NOTE]
    嚴格模式和混雜模式都是瀏覽器的呈現模式,瀏覽器究竟使用混雜模式還是嚴格模式呈現頁面與網頁中的DTD(文件類型定義)有關,DTD裡面包含了文檔的規則。比如:loose.dtd

    • 嚴格模式:又稱標準模式,是指瀏覽器按照W3C標準來解析代碼,呈現頁面
    • 混雜模式:又稱為怪異模式或者兼容模式,是指瀏覽器按照自己的方式來解析代碼,使用一種比較寬鬆的向後兼容的方式來显示頁面。

    10.3 瀏覽器的渲染過程?

    10.3.1 開始進行DOM解析,渲染DOM Tree

    10.3.2 開始進行CSS解析,渲染CSSOM Tree

    10.3.3 DOM樹和CSS樹的結合,最後會轉換為Render Tree

    10.3.4 Layout的過程,計算每一個DOM元素的位置、寬度、高度等信息,最終渲染並显示頁面到瀏覽器

    10.4 何時會觸發Reflow?

    [!NOTE]
    定義:DOM結構中每個元素都有自己的盒子模型,這些都是需要根據各種樣式來計算並根據計算結果將元素放在它該出現的位置,這個過程就是reflow;

    1. 當你增加、刪除、修改DOM節點的時候,會導致Reflow或Repaint
    2. 當你移動DOM的位置,或者設置動畫的時候
    3. 當你修改CSS樣式的時候
    4. 當你Resize窗口的時候(移動端沒有這個問題,與瀏覽器有關),或者在滾動窗口的時候
    5. 當你修改網頁的默認的字體的時候

    10.5 何時回觸發Repaint?

    [!NOTE]
    定義:當各種盒子的位置、大小以及其他屬性,例如顏色、字體大小都確定下來以後,瀏覽器於是便按照元素各自的特性繪製了一遍,於是頁面的內容出現了,這個過程就是repaint

    1. DOM改動
    2. CSS改動

    10.6 如何最大程度上的減少瀏覽器的重繪Repaint過程(頻率)呢?

    10.6.1 避免在document上直接進行頻繁的DOM操作,如果確實需要可以採用off-document的方式進行

      1. 先將元素從document中刪除,完成修改之後然後再把元素放回原來的位置
      1. 將元素的display設置為none, 然後完成修改之後再把元素的display屬性修改為原來的值
      1. 如果需要創建多個DOM節點,可以使用DocumentFragment創建完畢之後一次性地加入document中去
      var frag = document.createDocumentFragment();
      frag.appendChild(dom);    /*每次創建的節點先放入DocumentFragment中*/

    10.6.2 集中修改樣式

    1. 盡可能少的修改元素style上的屬性
    2. 盡量通過修改className來修改樣式(一次性修改)
    3. 通過cssText屬性來設置樣式值
      document.getElementById("d1").style.cssText = "color:red; font-size:13px;";

    10.6.3 緩存Layout的屬性值

    [!NOTE]
    對於Layout屬性中非引用類型的值(数字型),如果需要多次訪問則可以在一次訪問時先存儲到局部變量中,之後都使用局部變量,這樣可以避免每次讀取屬性時造成瀏覽器的渲染。

      var width = el.offsetWidth;
      var scrollLeft = el.scrollLeft;

    10.6.4 設置元素的position為absolute或fixed

    [!NOTE]
    在元素的position為static和relative時,元素處於DOM樹結構當中,當對元素的某個操作需要重新渲染時,瀏覽器會渲染整個頁面。將元素的position設置為absolute和fixed可以使元素從DOM樹結構中脫離出來獨立的存在,而瀏覽器在需要渲染時只需要渲染該元素以及位於該元素下方的元素,從而在某種程度上縮短瀏覽器渲染時間。

    11. 布局Layout?

    Layout屬性包括

    1. offsetLeft、offsetTop、offsetHeight、offsetWidth: 相對於父對象的邊距信息,且返回值為数字;left獲取或設置相對於具有定位屬性(position定義為relative)的父對象的邊距信息,返回值為字符串10px
    2. scrollTop/Left/Width/Height:滾動條在各個方向上拉動的距離,返回值為数字
    3. clientTop/Left/Width/Height:瀏覽器的可視區域的大小
    4. getComputedStyle()、currentStyle(in IE):瀏覽器渲染DOM元素之後的寬度和高度等樣式信息

    12. JS運行機制

    12.1 如何理解JS的單線程?

    看代碼,寫結果?

      // 同步任務
      console.log(1);
      // 異步任務要掛起
      setTimeout(function(){
        console.log(2)
      }, 0);
      console.log(3)
      // out : 1 3 2
      console.log('A');
      setTimeout(function(){
        console.log('B')
      }, 0);
      while (true) {
    
      }
    
      // out : A

    12.2 什麼是任務隊列?

      for (var i = 0; i < 4; i++) {
        // setTimeout , setInterval 只有在時間到了的時候,才會把這個事件放在異步隊列中去
        setTimeout(function(){
          console.log(i);
        }, 1000);
      }
      // out : 4 4 4 4

    12.3 什麼是Event Loop?

    [!NOTE]
    JS是單線程的,瀏覽器引擎會先來執行同步任務,遇到異步任務之後,會把當前的這個異步任務放在time模塊中,等到主線程中的所有的同步任務全部執行完畢之後;然後當前的這個異步任務只有時間到了之後,才會把這個任務(回調函數)放在一個異步隊列中;噹噹前的任務棧中的任務全部執行完畢了之後,會先去執行微任務隊列中的任務(Promise),然後等到微任務隊列中的所有任務全部執行完畢之後,再去執行process.nextTick()這個函數,等到這個函數執行完畢之後,本次的事件輪訓結束;
    開啟新的執行棧,從宏任務隊列中依次取出異步任務,開始執行;每個宏任務執行都會重新開啟一個新的任務執行棧

    12.3.1 3個關鍵點

    1. 執行棧執行的是同步任務;
    2. 什麼時候去異步隊列中取這個任務;
    3. 什麼時候向這個任務隊列中放入新的異步任務

      12.3.2 異步任務的分類

    • setTimeout, setInterval;
    • DOM事件(點擊按鈕的時候也會先去執行同步任務);
    • Promise

    13. 知識點總結

    1. 理解JS的單線程的概念
    2. 理解任務隊列
    3. 理解Event Loop
    4. 理解哪些語句會翻入到異步任務隊列
    5. 理解與放入到異步任務隊列的時機

      13.1 頁面性能

      13.1.1 提升頁面性能的方法有哪些?

    6. 資源壓縮合併,減少HTTP請求;
    7. 非核心代碼的異步加載 —> 異步加載的方式有哪些? —> 異步加載的區別?
    8. 利用瀏覽器的緩存 —> 緩存的分類 —> 緩存的原理
    9. 使用CDN加速
    10. 預解析DNS:DNS Prefetch 是一種DNS 預解析技術,當你瀏覽網頁時,瀏覽器會在加載網頁時對網頁中的域名進行解析緩存,這樣在你單擊當前網頁中的連接時就無需進行DNS的解析,減少用戶等待時間,提高用戶體驗。(提前解析域名,而不是點擊鏈接的時候才去進行DNS域名解析,可以節省DNS解析需要耗費的20-120毫秒時間)

      <!-- https協議的網站,默認是關閉了DNS的預解析的,可以使用下面的語句開啟  -->
      <meta http-equiv="x-dns-prefetch-control" content="on">
      <!-- 開始配置需要進行DNS預解析的域名 -->
      <link rel="dns-prefetch" href="//www.zhix.net">                               <!--支持http和HTTPS-->
      <link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />               <!--支持http的協議-->
      <link rel="dns-prefetch" href="http://nsclick.baidu.com" />
      <link rel="dns-prefetch" href="http://hm.baidu.com" />
      <link rel="dns-prefetch" href="http://eiv.baidu.com" />

    14. 異步加載的方式

    14.1 動態腳本的加載

      var script = document.createElement('script');
      document.getElementsByTagName('head')[0].appendChild(script);
    
      // 沒有 defer 或 async,瀏覽器會立即加載並執行指定的腳本,“立即”指的是在渲染該 script 標籤之下的文檔元素之前,也就是說不等待後續載入的文檔元素,讀到就加載並執行。
      <script src="script.js"></script>

    14.2 defer

    <!-- 有 defer,加載後續文檔元素的過程將和 script.js 的加載并行進行(異步),但是 script.js 的執行要在所有元素解析完成之後,DOMContentLoaded 事件觸發之前完成。 -->
    <script defer src="myscript.js"></script>

    14.3 async

      <!-- 有 async,加載和渲染後續文檔元素的過程將和 script.js 的加載與執行并行進行(異步)。 -->
      <script async src="script.js"></script>

    14.4 異步加載的區別?

    [!NOTE]

    1. defer是在HTML解析完成之後(DOMContentLoaded事件執行之後)才會執行,如果是多個,會按照加載的順序依次執行(按照順序執行)
    2. async是在加載完之後立即執行,如果是多個,執行順序和加載順序無關(與順序無關)

    15. 說一下瀏覽器的緩存機制吧?

    15.1 緩存的分類

    [!NOTE]
    緩存目的就是為了提升頁面的性能

    15.1.1 強緩存

    直接從本地讀取,不發送請求

        Response Headers
        cache-control: max-age=315360000(相對時間,優先級比expires高)
        expires: Sat, 10 Mar 2029 04:01:39 GMT(絕對時間)

    15.1.2 協商緩存

    問一下服務器,這個文件有沒有過期,然後再使用這個文件

        Response Headers
        last-modified: Tue, 12 Mar 2019 06:22:34 GMT(絕對時間)
        etag: "52-583dfb6f4de80"

    向服務器請求資源的時候,帶上if-Modified-Since或者if-None-Match這個請求頭,去詢問服務器:

        Request Headers
        if-Modified-Since: Tue, 12 Mar 2019 06:22:34 GMT
        if-None-Match: "52-583dfb6f4de80"

    16. 錯誤監控/如何保證前端產品的上線質量?

    16.1 前端錯誤的分類?

    1. 即時運行錯誤:代碼錯誤
    2. 資源加載錯誤:圖片/css/js文件加載失敗

    16.2 錯誤的捕獲方式?

    16.2.1 即時運行錯誤的捕獲方式

      // 方法一:使用try catch捕獲
      try {
        // ...
      } catch (e) {
        // error
      } finally {
        // handle error
      }
    
      // 方法二:使用window.onerror 捕獲錯誤
      // 無法捕獲到資源加載錯誤
      window.onerror = function(msg, url, line, col, error){
        // ...
      }  
      window.addEventListener('error', function(msg, url, line, col, error){
        // ...
      })

    16.2.2 資源加載錯誤(不會向上冒泡)

      // 方法一: 直接在script, img這些DOM標籤上面直接加上onerror事件
      Object.onerror = function(e){
          // ...
      }
    
      // 方法二:window.performace.getEntries(間接獲取資源加載錯誤的數量)
      var loadedResources = window.performance.getEntries();           // 1. 獲取瀏覽器中已經加載的所有資源(包括各個階段的詳細加載時間)
      var loaderImgs = loadedResources.filter(item => {
          return /\.jpg|png|gif|svg/.test(item.name)
      });
      var imgs = document.getElementsByTagName('img');                // 2. 獲取頁面中所有的img集合
      var len = imgs.length - loaderImgs.length;                      // 3. 加載失敗的圖片數量
      console.log('圖片加載失敗數量:', len, '條');
    
    
      // 方法三: 使用事件捕獲的方式來實現Error事件捕獲
      // 使用事件捕獲的方式來實現資源加載錯誤的事件的捕獲:window ---> document --> html --- > body ---> div ---...
      window.addEventListener('error', function (msg) {
          console.log(msg);
      }, true);

    16.2.3 補充的方法

          // 使用事件捕獲的方式來實現
         window.addEventListener('error', function (msg) {
             console.log('資源加載異常成功捕獲:', msg);
         }, true);
         // 使用事件冒泡的方式是只能捕獲到運行的時候的一些異常
         window.addEventListener('error', function (e) {
             console.log('運行異常成功捕獲1:', e.message, e.filename, e.lineno, e.colno, e.error);
         }, false);
    
         // 這種方式是可以按照參數的方式來接受相關的參數信息
         window.onerror = function (msg, url, line, col, error) {
             console.log('運行異常成功捕獲2:', msg, url, line, col, error);
         }

    16.2.4 問題的延伸:跨域的js運行錯誤可以捕獲嗎,錯誤提示是什麼?應該怎麼處理呢?

    16.2.4.1 錯誤信息

    errorinfo :
    Script0 error
    0 row
    0 col
    16.2.4.2 處理方法
    1. 第一步:在script標籤上增加crossorigin屬性
      <!-- script 表情添加crossorigin屬性 -->
      <!-- 除了 script,所有能引入跨域資源的標籤包括 link 和 img 之類,都有一樣的屬性 -->
      <script crossorigin  src="http://www.lmj.com/demo/crossoriginAttribute/error.js"></script>
    1. 第二步:設置js資源響應頭’Access-Control-Allow-Origin: * ‘,服務器端需要開啟
      // 服務器可以直接設置一個響應頭信息
      res.setResponseHeader('Access-Control-Allow-Origin', 'www.lmj.com');

    16.3 上報錯誤的基本原理?

    1. 採用Ajax通信的方式來上報
    2. 利用Image對象進行上報(cnzz)[重點理解掌握]
      // 下面的兩種方式都是可以實現錯誤信息的上報功能的
      (new Image).src = 'http://www.baidu.com?name=zhangsna&age=18&sex=male'
      (new Image()).src = 'https://www.baidu.com?name=zhangsan'

    17. 如何使用JS獲取客戶端的硬件信息呢?

      // IE 瀏覽器提供的獲取電腦硬件的API
      var locator = new ActiveXObject ("WbemScripting.SWbemLocator");
      var service = locator.ConnectServer(".");
      var properties = service.ExecQuery("SELECT * FROM Win32_Processor");

    18. 使用window.performace 來實現用戶體驗的數據記錄呢?

    [!NOTE]
    可以參考性能優化章節-performance性能監控一文內容。

    三四面(業務項目面)

    [!NOTE]

    • 知識面要廣
    • 理解要深刻
    • 內心要誠實:沒了解過,問面試官有哪些資料可以學習
    • 態度要謙虛
    • 回答要靈活:把握一個度,不要和面試官爭執對錯
      • 要學會讚美:被問住了可以回答,適當讚美(沒面試官理解的那麼深,虛心請教)

    19.介紹一下你做過的項目?

    19.1 項目介紹模板(業務能力體現)

    1. 我做過什麼業務?
    2. 負責的業務有什麼業績?
    3. 使用了什麼技術方案?
    4. 突破了什麼技術難點?
    5. 遇到了什麼問題?
    6. 最大的收穫是什麼?

    19.2 團隊協作能力

    19.3 事務推動能力

    19.4 帶人能力

    終面(HR面)

    20. 技術終面或HR面試要點

    [!NOTE]
    主要考察點:樂觀积極、主動溝通、邏輯順暢、上進有責任心、有主張,做事果斷、職業競爭力、職業規劃

    20.1 職業競爭力

    1. 業務能力:可以做到行業第一

    2. 思考能力:對同一件事可以從不同角度去思考,找到最優解

    3. 學習能力:不斷學習新的業務,沉澱、總結

    4. 無上限的付出:對於無法解決的問題可以熬夜、加班

    20.2 職業規劃

    1. 目標是什麼:在業務上成為專家,在技術上成為行業大牛

    2. 近階段的目標:不斷的學習積累各方面地經驗,以學習為主

    3. 長期目標:做幾件有價值的事情,如開源作品、技術框架等

    4. 方式方法:先完成業務上的主要問題,做到極致,然後逐步向目標靠攏

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

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

  • [學習筆記] 在Eclipse中使用Hibernate,並創建第一個Demo工程,數據庫為Oracle XE

    [學習筆記] 在Eclipse中使用Hibernate,並創建第一個Demo工程,數據庫為Oracle XE

    前文參考:

    在Eclipse中使用Hibernate

    安裝 Hibernate Tools 插件

    https://tools.jboss.org/downloads/

    Add the following URL to your Eclipse 4.13 (2019-09) installation, via:

    Help > Install New Software… > Work with:

    http://download.jboss.org/jbosstools/photon/stable/updates/

    Then select the individual features that you want to install:

    點擊Next

    點擊Next
    同意相關協議,點擊Finish .

    則會開始下載安裝。

    視網絡速度,可能需要幾分鐘到十幾分鐘的時間才能完成安裝。

    最後會提示重啟Eclipse才能生效。

    在Eclipse中新建Hibernate應用

    File->New -> Java Project

    點擊Finish

    項目結構圖

    在Eclipse中新建用戶庫

    此時下面显示了已經建立的用戶庫列表

    我們要添加Hibernate的依賴庫,因此點擊用戶庫

    Hibernate_4.3.5_final

    選擇jar文件

    項目結構圖

    繼續配置Hibernate

    最後自動形成 如下的文件內容:[本例使用oracle數據庫]

    Oracle 11g xe 在windows安裝請看如下鏈接:

    hibernate.cfg.xml

     <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
        <session-factory>
            <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
            <property name="hibernate.connection.password">123456</property>
            <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:xe</property>
            <property name="hibernate.connection.username">test</property>
            <property name="hibernate.default_schema">test</property>
            <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
        </session-factory>
    </hibernate-configuration>

    再增加幾個屬性

    配置文件更新后的內容如下: 注意要去掉name屬性 更改 為

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                                             "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
     <session-factory >
      <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
      <property name="hibernate.connection.password">123456</property>
      <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:xe:orcl</property>
      <property name="hibernate.connection.username">test</property>
      <property name="hibernate.default_schema">test</property>
      <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
      <property name="hibernate.show_sql">true</property>
      <!-- 第一次加載hibernate時根據model類會自動建立起表的結構(前提是先建立好數據庫),以後加載hibernate時根據 model類自動更新表結構,即使表結構改變了但表中的行仍然存在不會刪除以前的行。要注意的是當部署到服務器后,表結構是不會被馬上建立起來的,是要等 應用第一次運行起來后才會。 -->
      <property name="hibernate.hbm2ddl.auto">update</property> 
      <property name="hibernate.format_sql">true</property>
      <property name="hibernate.default_entity_mode">pojo</property>
     </session-factory>
    </hibernate-configuration>

    繼續完善工程Hibernate_demo_001

    新建一個包:mytest001.demo

    在包mytest001.demo之下新建一個PO類: Emp

    package mytest001.demo;
    
    public class Emp {
        // 員工的標識屬性
        private Integer id;
        // 姓名
        private String name;
        // 年齡
        private Integer age;
        // 工資 (分)
        private Integer salary;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Integer getSalary() {
            return salary;
        }
    
        public void setSalary(Integer salary) {
            this.salary = salary;
        }
    
        @Override
        public String toString() {
            return "Emp [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
        }
    }
    
    

    此刻此PO Emp.java 尚不具備持久化能力。下面為其添加註解。

    @Entity 註解聲明該類是一個Hibernate持久化類
    @Table 指定該類映射的表,對應的數據庫表名是T_EMP
    @Id 指定該類的標識屬性,映射到數據庫的主鍵列
    @GeneratedValue(strategy=GenerationType.SEQUENCE) 指定了主鍵生成策略,由於本文使用Oracle Database, 因此指定了使用 SEQUENCE

    在hibernate.cfg.xml中增加持久化映射類名

    增加一個新類:EmpManager,用於管理員工。

    package mytest001.demo;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.service.Service;
    import org.hibernate.service.ServiceRegistry;
    import org.hibernate.service.ServiceRegistryBuilder;
    
    
    public class EmpManager {
    
        public static void main(String[] args)  throws Exception  {
             
        //實例化配置
        Configuration configuration  = new Configuration()
                //不帶參數則默認加載hibernate.cfg.xml
                .configure();
        
        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                .applySettings(configuration.getProperties()).build();
        SessionFactory sFactory = configuration.buildSessionFactory(serviceRegistry);
        
        //創建session 
        Session session = sFactory.openSession();
        
        //開始事務
        Transaction tx = session.beginTransaction();
        //創建員工對象
        Emp emp = new Emp();
        // 設置員工信息
        emp.setAge(28);
        emp.setName("scott");
        emp.setSalary(10000);
        session.save(emp);
        // 提交事務
        tx.commit();
        session.close();
        sFactory.close();
                
        
        }
    
    }

    最後配置文件的內容:hibernate.cfg.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                                             "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
     <session-factory >
      <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
      <property name="hibernate.connection.password">123456</property>
      <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:xe</property>
      <property name="hibernate.connection.username">test</property>
      <property name="hibernate.default_schema">test</property>
      <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
      <property name="hibernate.show_sql">true</property>
      <!-- 第一次加載hibernate時根據model類會自動建立起表的結構(前提是先建立好數據庫),以後加載hibernate時根據 model類自動更新表結構,即使表結構改變了但表中的行仍然存在不會刪除以前的行。要注意的是當部署到服務器后,表結構是不會被馬上建立起來的,是要等 應用第一次運行起來后才會。 -->
      <property name="hibernate.hbm2ddl.auto">update</property> 
      <property name="hibernate.format_sql">true</property>
      <property name="hibernate.default_entity_mode">pojo</property>
      <mapping class="mytest001.demo.Emp"/>
     </session-factory>
    </hibernate-configuration>
    

    Emp.java

    package mytest001.demo;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    @Entity
    @Table(name="T_EMP")
    public class Emp {
        // 員工的標識屬性
        @Id
        @GeneratedValue(strategy=GenerationType.SEQUENCE)
        private Integer id;
        // 姓名
        private String name;
        // 年齡
        private Integer age;
        // 工資 (分)
        private Integer salary;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Integer getSalary() {
            return salary;
        }
    
        public void setSalary(Integer salary) {
            this.salary = salary;
        }
    
        @Override
        public String toString() {
            return "Emp [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
        }
    }
    

    最後的工程結構如下:

    運行EmpManger

    十一月 23, 2019 8:50:47 上午 org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
    INFO: HCANN000001: Hibernate Commons Annotations {4.0.4.Final}
    十一月 23, 2019 8:50:47 上午 org.hibernate.Version logVersion
    INFO: HHH000412: Hibernate Core {4.3.5.Final}
    十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Environment <clinit>
    INFO: HHH000206: hibernate.properties not found
    十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Environment buildBytecodeProvider
    INFO: HHH000021: Bytecode provider name : javassist
    十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Configuration configure
    INFO: HHH000043: Configuring from resource: /hibernate.cfg.xml
    十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Configuration getConfigurationInputStream
    INFO: HHH000040: Configuration resource: /hibernate.cfg.xml
    十一月 23, 2019 8:50:47 上午 org.hibernate.cfg.Configuration doConfigure
    INFO: HHH000041: Configured SessionFactory: null
    十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
    WARN: HHH000402: Using Hibernate built-in connection pool (not for production use!)
    十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
    INFO: HHH000401: using driver [oracle.jdbc.driver.OracleDriver] at URL [jdbc:oracle:thin:@localhost:1521:xe]
    十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
    INFO: HHH000046: Connection properties: {user=test, password=****}
    十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
    INFO: HHH000006: Autocommit mode: false
    十一月 23, 2019 8:50:47 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
    INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
    十一月 23, 2019 8:50:48 上午 org.hibernate.dialect.Dialect <init>
    INFO: HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect
    十一月 23, 2019 8:50:48 上午 org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
    INFO: HHH000399: Using default transaction strategy (direct JDBC transactions)
    十一月 23, 2019 8:50:48 上午 org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
    INFO: HHH000397: Using ASTQueryTranslatorFactory
    十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute
    INFO: HHH000228: Running hbm2ddl schema update
    十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute
    INFO: HHH000102: Fetching database metadata
    十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute
    INFO: HHH000396: Updating schema
    十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
    INFO: HHH000262: Table not found: T_EMP
    十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
    INFO: HHH000262: Table not found: T_EMP
    十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
    INFO: HHH000262: Table not found: T_EMP
    十一月 23, 2019 8:50:48 上午 org.hibernate.tool.hbm2ddl.SchemaUpdate execute
    INFO: HHH000232: Schema update complete
    Hibernate: 
        select
            test.hibernate_sequence.nextval 
        from
            dual
    Hibernate: 
        insert 
        into
            test.T_EMP
            (age, name, salary, id) 
        values
            (?, ?, ?, ?)
    十一月 23, 2019 8:50:48 上午 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
    INFO: HHH000030: Cleaning up connection pool [jdbc:oracle:thin:@localhost:1521:xe]
    

    到數據庫中查詢表:(這個表會被自動創建)
    select * from t_emp;

    如果再次運行會增加新的記錄。

    至此,本文完成。

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

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

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 阿里雲ECS服務器部署HADOOP集群(一):Hadoop完全分佈式集群環境搭建,阿里雲ECS服務器部署HADOOP集群(二):HBase完全分佈式集群搭建(使用外置ZooKeeper),阿里雲ECS服務器部署HADOOP集群(三):ZooKeeper 完全分佈式集群搭建,阿里雲ECS服務器部署HADOOP集群(四):Hive本地模式的安裝,阿里雲ECS服務器部署HADOOP集群(六):Flume 安裝,阿里雲ECS服務器部署HADOOP集群(七):Sqoop 安裝

    阿里雲ECS服務器部署HADOOP集群(一):Hadoop完全分佈式集群環境搭建,阿里雲ECS服務器部署HADOOP集群(二):HBase完全分佈式集群搭建(使用外置ZooKeeper),阿里雲ECS服務器部署HADOOP集群(三):ZooKeeper 完全分佈式集群搭建,阿里雲ECS服務器部署HADOOP集群(四):Hive本地模式的安裝,阿里雲ECS服務器部署HADOOP集群(六):Flume 安裝,阿里雲ECS服務器部署HADOOP集群(七):Sqoop 安裝

    準備:

    兩台配置CentOS 7.3的阿里雲ECS服務器;

    hadoop-2.7.3.tar.gz安裝包;

    jdk-8u77-linux-x64.tar.gz安裝包;

    hostname及IP的配置:

    更改主機名:

    由於系統為CentOS 7,可以直接使用‘hostnamectl set-hostname 主機名’來修改,修改完畢后重新shell登錄或者重啟服務器即可。

    1 hostnamectl set-hostname master
    2 exit
    3 ssh root@master
    1 hostnamectl set-hostname slave1
    2 exit 3 ssh root@slave1

    設置本地域名:

    設置本地域名這一步非常關鍵,ip的本地域名信息配置不好,即有可能造成Hadoop啟動出現問題,又有可能造成在使用Hadoop的MapReduce進行計算時報錯。在ECS上搭建Hadoop集群環境需參考以下兩篇文章:

    總結一下那就是,在“/etc/hosts”文件中進行域名配置時要遵從2個原則:

    •  新加域名在前面: 將新添加的Master、Slave服務器ip域名(例如“test7972”),放置在ECS服務器原有本地域名(例如“iZuf67wb***************”)的前面。但是注意ECS服務器原有本地      域名(例如“iZuf67wb***************”)不能被刪除,因為操作系統別的地方還會使用到。
    •  IP本機內網,其它外網: 在本機上的操作,都要設置成內網ip;其它機器上的操作,要設置成外網ip。

    master

    slave1

    此處摘自 

    配置好后需要在各個節點上執行如下命令,測試是否相互 ping 得通,如果 ping 不通,後面就無法順利配置成功:

    1 ping master -c 3
    2 ping slave1 -c 3

    例如我在 master 節點上 ping slave1 ,ping 通的話會显示 time,显示的結果如下圖所示:

    各節點角色分配

    master: NameNode  ResourceManager

    slave1: DataNode NodeManager

    免密碼登錄配置

    分別在 master 和 slave1 上做如下操作

    1 ssh-keygen -t rsa
    2 ssh-copy-id master 3 ssh-copy-id slave1

    驗證

    ssh master date;ssh slave1 date

    配置JDK

    解壓JDK安裝包到/usr/local/下

    tar -zxvf jdk-8u77-linux-x64.tar.gz -C /usr/local/

    將解壓目錄改為 jdk1.8

    mv jdk1.8.0_77/ jdk1.8/

    設置JAVA_HOME到系統環境變量

    vim /etc/profile

    在最後加入以下兩行代碼

    export JAVA_HOME=/usr/local/jdk1.8
    export PATH=$PATH:$JAVA_HOME/bin

    重新加載環境

    source /etc/profile

    這樣 master 的jdk就配置好了,可以用命令 java -version 測試下。

    java -version

    下面只需將 master 上配置好的文件分發到 slave1 上即可。

    將/usr/local/jdk1.8分發到 slave1 的/usr/local/下(建議壓縮后再分發)

    scp -r /usr/local/jdk1.8/ slave1:/usr/local/

    將/etc/profile分發到 slave1 的/etc/下

    scp /etc/profile slave1:/etc/

      然後重新加載 slave1 環境便完成了 slave1 的jdk配置

    source /etc/profile

    hadoop集群配置

    1 cd ~
    2 tar -zxvf hadoop-2.7.3.tar.gz -C /usr/local # 解壓到/usr/local中
    3 cd /usr/local/
    4 mv ./hadoop-2.7.3/ ./hadoop            # 將文件夾名改為hadoop

    輸入如下命令來檢查 Hadoop 是否可用,成功則會显示 Hadoop 版本信息:

    1 cd /usr/local/hadoop
    2 ./bin/hadoop version

    添加 HADOOP_HOME 到系統環境變量

    vim /etc/profile

    在後面添加如下兩行

    1 export HADOOP_HOME=/usr/local/hadoop
    2 export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin

    重新加載環境,並輸出變量 HADOOP_HOME 驗證

    進入/user/local/hadoop/etc/hadoop/可以看到如下配置文件

    集群/分佈式模式需要修改 /usr/local/hadoop/etc/hadoop 中的6個配置文件,更多設置項可點擊查看官方說明,這裏僅設置了我完成課堂作業所必須的設置項:hadoop-env.sh, slaves,  ,  ,  ,   。

    1.首先來配置 hadoop-env.sh ,只需要設置一下JAVA_HOME即可

    注:之前在配置jdk中配置的是基於系統的JAVA_HOME變量,這裏需要配置基於Hadoop集群的JAVA_HOME變量。

    hadoop-env.sh 是Hadoop的環境變量配置腳本。

    所以應做以下修改 vim hadoop-env.sh

    export JAVA_HOME=/usr/local/jdk1.8

    2.配置 slave , 指定 slave 節點

    sudo vi slaves

    刪去原有的 localhost , 添加將作為 slave 節點的 slave1

    3.配置 core-site.xml 

     1 <configuration>
     2 
     3     <property>
     4         <name>fs.defaultFS</name>
     5         <value>hdfs://master:9000</value>
     6         <description>The name of the default file system.</description>
     7     </property> 
     8 # 設置訪問hdfs的默認名,9000是默認端口
     9 
    10     <property>
    11         <name>hadoop.tmp.dir</name>
    12         <value>/usr/local/hadoop/tmp</value>
    13         <description>Abase for other temporary directories.</description>
    14     </property>
    15 # 在hdfs格式化的時候會自動創建相應的目錄 'tmp/' 16 17 <property> 18 <name>fs.trash.interval</name> 19 <value>4320</value> 20 <description>Number of minutes after which the checkpoint gets deleted.</description> 21 </property> 22 # 設置回收站里的文件保留時間(單位:秒) 23 24 </configuration>

    4.配置 hdfs-site.xml 

     1 <configuration>
     2 
     3     <property>
     4         <name>dfs.namenode.name.dir</name>
     5         <value>/usr/local/hadoop/tmp/dfs/name</value>
     6     </property>
     7 
     8     <property>
     9         <name>dfs.datanode.data.dir</name>
    10         <value>/usr/local/hadoop/tmp/dfs/data</value>
    11     </property>
    12 
    13     <property>
    14         <name>dfs.replication</name>
    15         <value>1</value>
    16     </property>
    17 # 副本,因為有一個 slave 節點這裏設置為1(一般偽分佈模式設1個,三個或三個以上節點設3個)
    18 
    19     <property>
    20         <name>dfs.permissions.enabled</name>
    21         <value>false</value>
    22         <description>If "true", enable permission checking in HDFS. If "false", permission checking is turned off, but all other behavior is unchanged. Switching from one parameter value to the other does not change the mode, owner or group of files or directories.</description>
    23     </property>
    24 
    25 </configuration>    

    5.配置 mapred-site.xml (這個文件沒有直接提供,而是提供了模版文件,需將模版文件轉換為配置文件) 

    1 sudo mv mapred-site.xml.template mapred-site.xml
    2 sudo vi mapred-site.xml
     1 <configuration>
     2 
     3     <property>
     4         <name>mapreduce.framework.name</name>
     5         <value>yarn</value>
     6         <description>The runtime framework for executing MapReduce jobs.Can be one of local, classic or yarn.</description>
     7     </property>
     8     <property>
     9         <name>mapreduce.jobtracker.http.address</name>
    10         <value>master:50030</value>
    11     </property>
    12     <property>
    13         <name>mapreduce.jobhisotry.address</name>
    14         <value>master:10020</value>
    15     </property>
    16     <property>
    17         <name>mapreduce.jobhistory.webapp.address</name>
    18         <value>master:19888</value>
    19     </property>
    20     <property>
    21         <name>mapreduce.jobhistory.done-dir</name>
    22         <value>/jobhistory/done</value>
    23     </property>
    24     <property>
    25         <name>mapreduce.jobhistory.intermediate-done-dir</name>
    26         <value>/jobhisotry/done_intermediate</value>
    27     </property>
    28     <property>
    29         <name>mapreduce.job.ubertask.enable</name>
    30         <value>true</value>
    31         <description>Whether to enable the small-jobs "ubertask" optimization,which runs "sufficiently small" jobs sequentially within a single JVM."Small" is defined by the following maxmaps, maxreduces, and maxbytes settings. Note that configurations for application masters also affect the "Small" definition - yarn.app.mapreduce.am.resource.mb must be larger than both mapreduce.map.memory.mb and mapreduce.reduce.memory.mb, and yarn.app.mapreduce.am.resource.cpu-vcores must be larger than both mapreduce.map.cpu.vcores and mapreduce.reduce.cpu.vcores to enable ubertask. Users may override this value.</description>
    32     </property>
    33 
    34 </configuration>

    6.配置 yarn-site.xml

     1 <configuration>
     2 
     3     <property>
     4         <name>yarn.resourcemanager.hostname</name>
     5         <value>master</value>
     6     </property>
     7     <property>
     8         <name>yarn.nodemanager.aux-services</name>
     9         <value>mapreduce_shuffle</value>
    10         <description>A comma separated list of services where service name should only contain a-zA-Z0-9_ and can not start with numbers</description>
    11     </property>
    12     <property>
    13         <name>yarn.resourcemanager.address</name>
    14         <value>master:18040</value>
    15     </property>
    16     <property>
    17         <name>yarn.resourcemanager.scheduler.address</name>
    18         <value>master:18030</value>
    19     </property>
    20     <property>
    21         <name>yarn.resourcemanager.resource-tracker.address</name>
    22         <value>master:18025</value>
    23     </property>
    24     <property>
    25         <name>yarn.resourcemanager.admin.address</name>
    26         <value>master:18141</value>
    27     </property>
    28     <property>
    29         <name>yarn.resourcemanager.webapp.address</name>
    30         <value>master:18088</value>
    31     </property>
    32     <property>
    33         <name>yarn.log-aggregation-enable</name>
    34         <value>true</value>
    35     </property>
    36     <property>
    37         <name>yarn.log-aggregation.retain-seconds</name>
    38         <value>86400</value>
    39     </property>
    40     <property>
    41         <name>yarn.log-aggregation.retain-check-interval-seconds</name>
    42         <value>86400</value>
    43     </property>
    44     <property>
    45         <name>yarn.nodemanager.remote-app-log-dir</name>
    46         <value>/tmp/logs</value>
    47     </property>
    48     <property>
    49         <name>yarn.nodemanager.remote-app-log-dir-suffix</name>
    50         <value>logs</value>
    51     </property>
    52 
    53 </configuration>

     到這裏 master 就已經配置好了,下面將該服務器的配置分發到 slave1 上去(建議壓縮后再分發),在此使用壓縮後分發的方法

    在 master 節點上執行

    1 cd /usr/local
    2 tar -zcvf ~/hadoop.master.tar.gz ./hadoop 3 cd ~ 4 scp ./hadoop.master.tar.gz slave1:/root/ 5 scp /etc/profile slave1:/etc/

    在 slave1 節點上執行

    tar -zxvf ~/hadoop.master.tar.gz -C /usr/local

    在 slave1 上重新加載環境並檢查驗證

    source /etc/profile
    echo $HADOOP_HOME

    HDFS NameNode 格式化(只要在 master 上執行即可)

    $HADOOP_HOME/bin/hdfs namenode -format

    看到下面的輸出,表明hdfs格式化成功

    INFO common.Storage: Storage directory /usr/local/hadoop/tmp/dfs/name has been successfully formatted.

    啟動前檢查防火牆狀態

    systemctl status firewalld

    我這裡是已經關閉的,若未關閉,可以參考下圖(來自)

    阿里雲服務器還需要在服務器安全組裡配置防火牆,需將配置文件里的相關端口全部添加,否則會出現 web 頁面打不開,以及 DataNode 啟動但 Live datenode 為 0 等問題

    啟動 Hadoop 集群

    $HADOOP_HOME/sbin/start-all.sh

     

    • 第一次啟動 hadoop 時會出現 ssh 提示,提示是否要連入 0.0.0.0 節點,輸入 yes 即可
    • 若出現 hadoop 啟動時 datanode 沒有啟動,可以參考來解決

    啟動 job history server

    在 master 上執行

    $HADOOP_HOME/sbin/mr-jobhistory-daemon.sh start historyserver

    成功后在兩個節點上驗證

    在 master 上 執行 

    jps

    可以看到 ResourceManager、SecondaryNameNode、NameNode、JobHistoryServer 四個進程全部啟動

    在 slave1 上執行

    jps

    可以看到 NodeManager、DataNode 兩個進程全部啟動

    缺少任一進程都表示出錯。另外還需要在 Master 節點上通過命令 hdfs dfsadmin -report 查看 DataNode 是否正常啟動,如果 Live datanodes 不為 0 ,則說明集群啟動成功。例如我這邊一共有 1 個 Datanodes:

    全部配置完成之後查看 web 頁面

    hdfs: http://master:50070/

    hdfs Datanode 節點信息

    hdfs 的情況

    hdfs 的文件情況

     yarn:http://master:18088/

     

    阿里雲ECS服務器部署HADOOP集群系列:

     

     

     

     

     

     

     

     

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

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

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

  • Vue項目使用CSS變量實現主題化

    主題化管理經常能在網站上看到,一般的思路都是將主題相關的CSS樣式獨立出來,在用戶選擇主題的時候加載相應的CSS樣式文件。現在大部分瀏覽器都能很好的兼容,主題化樣式更容易管理了。最近,使用CSS變量在Vue項目中做了一個主題化實踐,下面來看看整個過程。

    可行性測試

    為了檢驗方法的可行性,在public文件夾下新建一個themes文件夾,並在themes文件夾新建一個default.css文件:

    :root {
      --color: red;
    }

    在public文件夾的index.html文件中引入外部樣式theme.css,如下:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <link rel="icon" href="<%= BASE_URL %>favicon.ico">
        <title>vue-skin-peeler-demo</title>
        <!-- 引入themes文件夾下的default.css -->
        <link rel="stylesheet" type="text/css" href="src/themes/default.css" rel="external nofollow">
      </head>
      <body>
        <noscript>
          <strong>We're sorry but vue-skin-peeler-demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
        </noscript>
        <div id="app"></div>
        <!-- built files will be auto injected -->
      </body>
    </html>

    然後,在Home.vue中使用CSS變量:

    <template>
      <div class="home">
        <div :class="$style.demo">變紅色</div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'home'
    }
    </script>
    
    <style module lang="scss">
      .demo {
        color: var(--color);
      }
    </style>

    然後,運行項目並在瀏覽器中打開頁面,頁面显示效果正常。

    注意:@vue/cli使用link標籤引入css樣式可能報錯“We’re sorry but vue-skin-peeler-demo doesn’t work properly without JavaScript enabled. Please enable it to continue.”。這是因為@vue/cli將src目錄下的文件都通過webpack打包所引起,所以,靜態文件資源要放在public(如果是@vue/cli 2.x版本放在static)文件夾下。

    實現主題切換

    這裏主題切換的思路是替換link標籤的href屬性,因此,需要寫一個替換函數,在src目錄下新建themes.js文件,代碼如下:

    // themes.js
    const createLink = (() => {
      let $link = null
      return () => {
        if ($link) {
          return $link
        }
        $link = document.createElement('link')
        $link.rel = 'stylesheet'
        $link.type = 'text/css'
        document.querySelector('head').appendChild($link)
        return $link
      }
    })()
    
    /**
     * 主題切換函數
     * @param {string} theme - 主題名稱, 默認default
     * @return {string} 主題名稱
     */
    const toggleTheme = (theme = 'default') => {
      const $link = createLink()
      $link.href = `./themes/${theme}.css`
      return theme
    }
    
    export default toggleTheme

    然後,在themes文件下創建default.css和dark.css兩個主題文件。創建CSS變量,實現主題化。CSS變量實現主題切換請參考另一篇文章

    兼容性

    IE瀏覽器以及一些舊版瀏覽器不支持CSS變量,因此,需要使用,是一個,可在舊版和現代瀏覽器中為CSS自定義屬性(也稱為“ CSS變量”)提供客戶端支持。由於要開啟watch監聽,所以還有安裝。

    安裝:

    npm install css-vars-ponyfill mutationobserver-shim --save

    然後,在themes.js文件中引入並使用:

    // themes.js
    import 'mutationobserver-shim'
    import cssVars from 'css-vars-ponyfill'
    
    cssVars({
      watch: true
    })
    
    const createLink = (() => {
      let $link = null
      return () => {
        if ($link) {
          return $link
        }
        $link = document.createElement('link')
        $link.rel = 'stylesheet'
        $link.type = 'text/css'
        document.querySelector('head').appendChild($link)
        return $link
      }
    })()
    
    /**
     * 主題切換函數
     * @param {string} theme - 主題名稱, 默認default
     * @return {string} 主題名稱
     */
    const toggleTheme = (theme = 'default') => {
      const $link = createLink()
      $link.href = `./themes/${theme}.css`
      return theme
    }
    
    export default toggleTheme

    開啟watch后,在IE 11瀏覽器點擊切換主題開關不起作用。因此,每次切換主題時都重新執行cssVars(),還是無法切換主題,原因是開啟watch后重新執行cssVars()是無效的。最後,只能先關閉watch再重新開啟。成功切換主題的themes.js代碼如下:

    // themes.js
    import 'mutationobserver-shim'
    import cssVars from 'css-vars-ponyfill'
    
    const createLink = (() => {
      let $link = null
      return () => {
        if ($link) {
          return $link
        }
        $link = document.createElement('link')
        $link.rel = 'stylesheet'
        $link.type = 'text/css'
        document.querySelector('head').appendChild($link)
        return $link
      }
    })()
    
    /**
     * 主題切換函數
     * @param {string} theme - 主題名稱, 默認default
     * @return {string} 主題名稱
     */
    const toggleTheme = (theme = 'default') => {
      const $link = createLink()
      $link.href = `./themes/${theme}.css`
      cssVars({
        watch: false
      })
      setTimeout(function () {
        cssVars({
          watch: true
        })
      }, 0)
      return theme
    }
    
    export default toggleTheme

    查看所有代碼,請移步。

    記住主題

    實現記住主題這個功能,一是可以向服務器保存主題,一是使用本地存儲主題。為了方便,這裏主要使用本地存儲主題的方式,即使用localStorage存儲主題。具體實現請移步。

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

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

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!