|
2011年8月7日
淘寶招聘hadoop工程師若干, 面向在校生(2014年畢業),工作地點:杭州或北京 Hadoop研發工程師 職位描述 您將負責: 1.預研、開發、測試hdfs/mapreduce/hive/hbase的功能、性能和擴展; 2.對有助于提升集群處理能力/高可用性/高擴展性的各種解決方案進行跟蹤和落地; 3.解決海量數據不斷增長面臨的挑戰,解決業務需求。 您需要具備: 1、熟練運用java語言; 2、熟悉jvm運行機制、熟悉linux; 3、至少熟悉hadoop、hbase、hive等軟件之一;
有意者請發送郵件到 yuling.sh@taobao.com
mapreduce中,一個job的map個數, 每個map處理的數據量是如何決定的呢? 另外每個map又是如何讀取輸入文件的內容呢? 用戶是否可以自己決定輸入方式, 決定map個數呢? 這篇文章將詳細講述hadoop中各種InputFormat的功能和如何編寫自定義的InputFormat. 簡介: mapreduce作業會根據輸入目錄產生多個map任務, 通過多個map任務并行執行來提高作業運行速度, 但如果map數量過少, 并行量低, 作業執行慢, 如果map數過多, 資源有限, 也會增加調度開銷. 因此, 根據輸入產生合理的map數, 為每個map分配合適的數據量, 能有效的提升資源利用率, 并使作業運行速度加快. 在mapreduce中, 每個作業都會通過 InputFormat來決定map數量. InputFormat是一個接口, 提供兩個方法: InputSplit[] getSplits(JobConf job, int numSplits) throws IOException; RecordReader<K, V> getRecordReader(InputSplit split, JobConf job, Reporter reporter) throws IOException; 其中getSplits方法會根據輸入目錄產生InputSplit數組, 每個InputSplit會相應產生一個map任務, map的輸入定義在InputSplit中. getRecordReader方法返回一個RecordReader對象, RecordReader決定了map任務如何讀取輸入數據, 例如一行一行的讀取還是一個字節一個字節的讀取, 等等. 下圖是InputFormat的實現類: (暫時無法上傳) 這理詳細介紹FileInputFormat和CombineFileInputFormat, 其它不常用,有興趣的可以自己查看hadoop源碼.
FileInputFormat(舊接口org.apache.hadoop.mapred) mapreduce默認使用TextInputFormat,TextInputFormat沒有實現自己的getSplits方法,它繼承于FileInputFormat, 因此使用了FileInputFormat的. org.apache.hadoop.mapred.FileInputFormat的getSplits流程: 兩個配置 mapred.min.split.size (一個map最小輸入長度), mapred.map.tasks (推薦map數量) 如何決定每個map輸入長度呢? 首先獲取輸入目錄下所有文件的長度和, 除以mapred.map.tasks得到一個推薦長度goalSize, 然后通過式子: Math.max(minSize, Math.min(goalSize, blockSize))決定map輸入長度. 這里的minSize為mapred.min.split.size, blockSize為相應文件的block長度. 這式子能保證一個map的輸入至少大于mapred.min.split.size, 對于推薦的map長度,只有它的長度小于blockSize且大于mapred.min.split.size才會有效果. 由于mapred.min.split.size默認長度為1, 因此通常情況下只要小于blockSize就有效果,否則使用blockSize做為map輸入長度. 因此, 如果想增加map數, 可以把mapred.min.split.size調小(其實默認值即可), 另外還需要把mapred.map.tasks設置大. 如果需要減少map數,可以把mapred.min.split.size調大, 另外把mapred.map.tasks調小. 這里要特別指出的是FileInputFormat會讓每個輸入文件至少產生一個map任務, 因此如果你的輸入目錄下有許多文件, 而每個文件都很小, 例如幾十kb, 那么每個文件都產生一個map會增加調度開銷. 作業變慢. 那么如何防止這種問題呢? CombineFileInputFormat能有效的減少map數量.
FileInputFormat(新接口org.apache.hadoop.mapreduce.lib.input) Hadoop 0.20開始定義了一套新的mapreduce編程接口, 使用新的FileInputFormat, 它與舊接口下的FileInputFormat主要區別在于, 它不再使用mapred.map.tasks, 而使用mapred.max.split.size參數代替goalSize, 通過Math.max(minSize, Math.min(maxSize, blockSize))決定map輸入長度, 一個map的輸入要大于minSize,小于 Math.min(maxSize, blockSize). 若需增加map數,可以把mapred.min.split.size調小,把mapred.max.split.size調大. 若需減少map數, 可以把mapred.min.split.size調大, 并把mapred.max.split.size調小. CombineFileInputFormat 顧名思義, CombineFileInputFormat的作用是把許多文件合并作為一個map的輸入. 在它之前,可以使用MultiFileInputFormat,不過其功能太簡單, 它以文件為單位,一個文件至多分給一個map處理, 如果某個目錄下有許多小文件, 另外還有一個超大文件, 處理大文件的map會嚴重偏慢. CombineFileInputFormat是一個被推薦使用的InputFormat. 它有三個配置: mapred.min.split.size.per.node, 一個節點上split的至少的大小 mapred.min.split.size.per.rack 一個交換機下split至少的大小 mapred.max.split.size 一個split最大的大小 它的主要思路是把輸入目錄下的大文件分成多個map的輸入, 并合并小文件, 做為一個map的輸入. 具體的原理是下述三步: 1.根據輸入目錄下的每個文件,如果其長度超過mapred.max.split.size,以block為單位分成多個split(一個split是一個map的輸入),每個split的長度都大于mapred.max.split.size, 因為以block為單位, 因此也會大于blockSize, 此文件剩下的長度如果大于mapred.min.split.size.per.node, 則生成一個split, 否則先暫時保留. 2. 現在剩下的都是一些長度效短的碎片,把每個rack下碎片合并, 只要長度超過mapred.max.split.size就合并成一個split, 最后如果剩下的碎片比mapred.min.split.size.per.rack大, 就合并成一個split, 否則暫時保留. 3. 把不同rack下的碎片合并, 只要長度超過mapred.max.split.size就合并成一個split, 剩下的碎片無論長度, 合并成一個split. 舉例: mapred.max.split.size=1000 mapred.min.split.size.per.node=300 mapred.min.split.size.per.rack=100 輸入目錄下五個文件,rack1下三個文件,長度為2050,1499,10, rack2下兩個文件,長度為1010,80. 另外blockSize為500. 經過第一步, 生成五個split: 1000,1000,1000,499,1000. 剩下的碎片為rack1下:50,10; rack2下10:80 由于兩個rack下的碎片和都不超過100, 所以經過第二步, split和碎片都沒有變化. 第三步,合并四個碎片成一個split, 長度為150. 如果要減少map數量, 可以調大mapred.max.split.size, 否則調小即可. 其特點是: 一個塊至多作為一個map的輸入,一個文件可能有多個塊,一個文件可能因為塊多分給做為不同map的輸入, 一個map可能處理多個塊,可能處理多個文件。
Yarn做為hadoop下一代集群資源管理和調度平臺, 其上能支持多種計算框架, 本文就簡要介紹一下這些計算框架.
1. MapReduce 首先是大家熟悉的mapreduce, 在MR2之前, hadoop包括HDFS和mapreduce, 做為hadoop上唯一的分布式計算框架, 其優點是用戶可以很方便的編寫分布式計算程序, 并支持許多的應用, 如hive, mahout, pig等. 但是其缺點是無法充分利用集群資源, 不支持DAG, 迭代式計算等. 為了解決這些問題, yahoo提出了Yarn (next generation mapreduce), 一個分布式集群集群資源管理和調度平臺. 這樣除了mapreduce外, 還可以支持各種計算框架. 2. Spark Spark是一種與mapreduce相似的開源計算框架, 不同之處在于Spark在某些工作負載方面表現更優, 因為它使用了內存分布式數據集, 另外除了提供交互式查詢外, 它還可以優化迭代工作負載. 3. Apache HAMA Apache Hama 是一個運行在HDFS上的BSP(Bulk Synchronous Parallel大容量同步并行) 計算框架, 主要針對大規模科學計算,如矩陣, 圖像, 網絡算法等.當前它有一下功能: - 作業提交和管理接口
- 單節點上運行多個任務
- 輸入/輸出格式化
- 備份恢復
- 支持通過Apache Whirr運行在云端
- 支持與Yarn一起運行
4. Apache Giraph 圖像處理平臺上運行這大型算法(如page rank, shared connections, personalization-based popularity 等)已經很流行, Giraph采用BSP模型(bulk-synchronous parallel model),可用于等迭代類算法。 5. Open MPI 這是一個高性能計算函數庫,通常在HPC(High Performance Computing)中采用,與MapReduce相比,其性能更高,用戶可控性更強,但編程復雜,容錯性差,可以說,各有所長,在實際應用中,針對不同 該應用會采用MPI或者MapReduce。 6. Apache HBase HBase是一個hadoop數據庫, 其特點是分布式,可擴展的,存儲大數據。當有需要隨機,實時讀寫的大數據時, 使用HBase很適合.
本文參考: http://wiki.apache.org/hadoop/PoweredByYarn http://www.oschina.net/p/open+mpi
http://incubator.apache.org/hama/ http://incubator.apache.org/giraph/ http://hbase.apache.org/
轉載 http://fujun.sinaapp.com/2011/11/02/68.html 第一步,打開終端,看看你的顯卡Ubuntu能認出多少顯示分辨率設置,輸入命令 wufujun@wufujun-VirtualBox:~$ xrandr
系統給出的結果 Screen 0: minimum 64 x 64, current 1024 x 768, maximum 32000 x 32000 VBOX0 connected 1024×768+0+0 0mm x 0mm 1024×768 60.0 + 60.0 1600×1200 60.0 1440×1050 60.0 1280×960 60.0 800×600 60.0 640×480 60.0 這里可以看到,沒有16:9的的分辨率設置 第二步,用cvt命令測試1368×768是否可用 wufujun@wufujun-VirtualBox:~$ cvt 1368 768
顯示結果如下 # 1368×768 59.88 Hz (CVT) hsync: 47.79 kHz; pclk: 85.86 MHz Modeline “1368x768_60.00″ 85.25 1368 1440 1576 1784 768 771 781 798 -hsync +vsync 從這個結果里可以到,16:9的分辨率是可以用的 第三步 輸入 wufujun@wufujun-VirtualBox:~$ sudo xrandr --newmode "1368x768" 85.86 1368 1440 1576 1784 768 771 781 798 -hsync +vsync
建立新的分辨率模式1368×768,把剛才cvt得到的數據寫進參數 第四步 繼續輸入 sudo xrandr --addmode VBOX0 "1368x768"
給當前顯示器VBOX0增加1368×768分辨率設置 做完以上操作后,可以在”顯示“設置里面看到顯示的分辨率列表中多了一個 1368×768(16:9)的選項。選中這個選項,點擊應用,完美的寬屏顯示回來了! 經過測試,上面的方法做完以后,每次注銷后就又變回了4:3的比例,而且會有的報錯,沒辦法,按上面的修改完畢后,還要再修改一下/etc/X11/xorg.conf這個文件,這個配置文件在現在的版里已經取消了,所以需要我們新建一個 $ sudo gedit /etc/X11/xorg.conf
編輯內容為: Section "Device" Identifier "Configured Video Device" EndSection
Section "Monitor" Identifier "Configured Monitor" Modeline "1368x768_60.00" 85.86 1368 1440 1584 1800 768 769 772 795 -HSync +Vsync EndSection Section "Screen" Identifier "Default Screen" Monitor "Configured Monitor" Device "Configured Video Device" SubSection "Display" Modes "1368x768@60" EndSubSection EndSection 其中 Modeline “1368x768_60.00″ 85.86 1368 1440 1584 1800 768 769 772 795 -HSync +Vsync 就是用$ cvt 1368 768得到的值。也可以用$ gtf 1368 768 60命令來得到這個Modeline的值,這個命令中,1368 768是分辨率 60為刷新率,用這個命令得到的值可能會更為準確一些。 SubSection "Display" Modes "1368x768@60" EndSubSection
這段是設置默認顯示最佳分辨率。 注意這段文件中的一些規則 Section “Device”區塊中,Identifier指定了顯卡的唯一名稱,這個名稱可以隨便取,但一定要與Section “Screen”區塊中的device選項中的名稱相同。在Section “Monitor”區塊中,Identifier指定了顯示器的唯一名稱,這個名稱可以隨便取,但一定要與Section “Screen”區塊中的Monitor選項中所指定的名稱相同。Section “Screen”區塊中的Identifier選項,指定了這個顯卡與顯示器相結合的唯一名稱。這個名稱也可以隨便取的。這個名稱需要與Section “ServerLayout” 區塊中的名稱相同。這個Section “ServerLayout” 區塊我們一般不必編寫
摘要: 最近這些天學習了classLoader的原理, 原因是因為服務器上的一個java進程啟動時加載兩個不同版本的jar包, 含有相同名字的類, 而且服務端的jar包排在前面, 我上傳的jar包排在后面, 于是每次都使用服務端的jar包, 我的jar包便無法生效, 因此希望修改classLader, 讓它按相反的順序加載jar包. ... 閱讀全文
摘要: High Availability for the HDFS Namenode Sanjay Radia, Suresh Srinivas Yahoo! Inc (本文為namdnoe HA的設計文檔翻譯) 1. 問題闡述 有許多方法可以改善HDFS Namednoe(NN)的可用性,包括減少啟動時間,更... 閱讀全文
本文轉自:http://blog.csdn.net/zhouysh/article/details/304767 JAVA代碼編寫的30條建議 (1) 類名首字母應該大寫。字段、方法以及對象(句柄)的首字母應小寫。對于所有標識符,其中包含的所有單詞都應緊靠在一起,而且大寫中間單詞的首字母。例如: ThisIsAClassName thisIsMethodOrFieldName 若在定義中出現了常數初始化字符,則大寫static final基本類型標識符中的所有字母。這樣便可標志出它們屬于編譯期的常數。 Java包(Package)屬于一種特殊情況:它們全都是小寫字母,即便中間的單詞亦是如此。對于域名擴展名稱,如com,org,net或者edu等,全部都應小寫(這也是Java 1.1和Java 1.2的區別之一)。
(2) 為了常規用途而創建一個類時,請采取"經典形式",并包含對下述元素的定義:
equals() hashCode() toString() clone()(implement Cloneable) implement Serializable
(3) 對于自己創建的每一個類,都考慮置入一個main(),其中包含了用于測試那個類的代碼。為使用一個項目中的類,我們沒必要刪除測試代碼。若進行了任何形式的改動,可方便地返回測試。這些代碼也可作為如何使用類的一個示例使用。
(4) 應將方法設計成簡要的、功能性單元,用它描述和實現一個不連續的類接口部分。理想情況下,方法應簡明扼要。若長度很大,可考慮通過某種方式將其分割成較短的幾個方法。這樣做也便于類內代碼的重復使用(有些時候,方法必須非常大,但它們仍應只做同樣的一件事情)。
(5) 設計一個類時,請設身處地為客戶程序員考慮一下(類的使用方法應該是非常明確的)。然后,再設身處地為管理代碼的人考慮一下(預計有可能進行哪些形式的修改,想想用什么方法可把它們變得更簡單)。 (6) 使類盡可能短小精悍,而且只解決一個特定的問題。下面是對類設計的一些建議: ■一個復雜的開關語句:考慮采用"多形"機制 ■數量眾多的方法涉及到類型差別極大的操作:考慮用幾個類來分別實現 ■許多成員變量在特征上有很大的差別:考慮使用幾個類
(7) 讓一切東西都盡可能地"私有"--private。可使庫的某一部分"公共化"(一個方法、類或者一個字段等等),就永遠不能把它拿出。若強行拿出,就可 能破壞其他人現有的代碼,使他們不得不重新編寫和設計。若只公布自己必須公布的,就可放心大膽地改變其他任何東西。在多線程環境中,隱私是特別重要的一個 因素--只有private字段才能在非同步使用的情況下受到保護。
(8) 謹惕"巨大對象綜合癥"。對一些習慣于順序編程思維、且初涉OOP領域的新手,往往喜歡先寫一個順序執行的程序,再把它嵌入一個或兩個巨大的對象里。根據編程原理,對象表達的應該是應用程序的概念,而非應用程序本身。
(9) 若不得已進行一些不太雅觀的編程,至少應該把那些代碼置于一個類的內部。
(10) 任何時候只要發現類與類之間結合得非常緊密,就需要考慮是否采用內部類,從而改善編碼及維護工作(參見第14章14.1.2小節的"用內部類改進代碼")。
(11) 盡可能細致地加上注釋,并用javadoc注釋文檔語法生成自己的程序文檔。
(12) 避免使用"魔術數字",這些數字很難與代碼很好地配合。如以后需要修改它,無疑會成為一場噩夢,因為根本不知道"100"到底是指"數組大小"還是"其他 全然不同的東西"。所以,我們應創建一個常數,并為其使用具有說服力的描述性名稱,并在整個程序中都采用常數標識符。這樣可使程序更易理解以及更易維護。
(13) 涉及構建器和異常的時候,通常希望重新丟棄在構建器中捕獲的任何異常--如果它造成了那個對象的創建失敗。這樣一來,調用者就不會以為那個對象已正確地創建,從而盲目地繼續。
(14) 當客戶程序員用完對象以后,若你的類要求進行任何清除工作,可考慮將清除代碼置于一個良好定義的方法里,采用類似于cleanup()這樣的名字,明確表 明自己的用途。除此以外,可在類內放置一個boolean(布爾)標記,指出對象是否已被清除。在類的finalize()方法里,請確定對象已被清除, 并已丟棄了從RuntimeException繼承的一個類(如果還沒有的話),從而指出一個編程錯誤。在采取象這樣的方案之前,請確定 finalize()能夠在自己的系統中工作(可能需要調用System.runFinalizersOnExit(true),從而確保這一行為)。
(15) 在一個特定的作用域內,若一個對象必須清除(非由垃圾收集機制處理),請采用下述方法:初始化對象;若成功,則立即進入一個含有finally從句的try塊,開始清除工作。
(16) 若在初始化過程中需要覆蓋(取消)finalize(),請記住調用super.finalize()(若Object屬于我們的直接超類,則無此必 要)。在對finalize()進行覆蓋的過程中,對super.finalize()的調用應屬于最后一個行動,而不應是第一個行動,這樣可確保在需要 基礎類組件的時候它們依然有效。
(17) 創建大小固定的對象集合時,請將它們傳輸至一個數組(若準備從一個方法里返回這個集合,更應如此操作)。這樣一來,我們就可享受到數組在編譯期進行類型檢查的好處。此外,為使用它們,數組的接收者也許并不需要將對象"造型"到數組里。
(18) 盡量使用interfaces,不要使用abstract類。若已知某樣東西準備成為一個基礎類,那么第一個選擇應是將其變成一個interface(接 口)。只有在不得不使用方法定義或者成員變量的時候,才需要將其變成一個abstract(抽象)類。接口主要描述了客戶希望做什么事情,而一個類則致力 于(或允許)具體的實施細節。
(19) 在構建器內部,只進行那些將對象設為正確狀態所需的工作。盡可能地避免調用其他方法,因為那些方法可能被其他人覆蓋或取消,從而在構建過程中產生不可預知的結果(參見第7章的詳細說明)。
(20) 對象不應只是簡單地容納一些數據;它們的行為也應得到良好的定義。
(21) 在現成類的基礎上創建新類時,請首先選擇"新建"或"創作"。只有自己的設計要求必須繼承時,才應考慮這方面的問題。若在本來允許新建的場合使用了繼承,則整個設計會變得沒有必要地復雜。
(22) 用繼承及方法覆蓋來表示行為間的差異,而用字段表示狀態間的區別。一個非常極端的例子是通過對不同類的繼承來表示顏色,這是絕對應該避免的:應直接使用一個"顏色"字段。
(23) 為避免編程時遇到麻煩,請保證在自己類路徑指到的任何地方,每個名字都僅對應一個類。否則,編譯器可能先找到同名的另一個類,并報告出錯消息。若懷疑自己碰到了類路徑問題,請試試在類路徑的每一個起點,搜索一下同名的.class文件。
(24) 在Java 1.1 AWT中使用事件"適配器"時,特別容易碰到一個陷阱。若覆蓋了某個適配器方法,同時拼寫方法沒有特別講究,最后的結果就是新添加一個方法,而不是覆蓋現 成方法。然而,由于這樣做是完全合法的,所以不會從編譯器或運行期系統獲得任何出錯提示--只不過代碼的工作就變得不正常了。
(25) 用合理的設計方案消除"偽功能"。也就是說,假若只需要創建類的一個對象,就不要提前限制自己使用應用程序,并加上一條"只生成其中一個"注釋。請考慮將 其封裝成一個"獨生子"的形式。若在主程序里有大量散亂的代碼,用于創建自己的對象,請考慮采納一種創造性的方案,將些代碼封裝起來。
(26) 警惕"分析癱瘓"。請記住,無論如何都要提前了解整個項目的狀況,再去考察其中的細節。由于把握了全局,可快速認識自己未知的一些因素,防止在考察細節的時候陷入"死邏輯"中。
(27) 警惕"過早優化"。首先讓它運行起來,再考慮變得更快--但只有在自己必須這樣做、而且經證實在某部分代碼中的確存在一個性能瓶頸的時候,才應進行優化。 除非用專門的工具分析瓶頸,否則很有可能是在浪費自己的時間。性能提升的隱含代價是自己的代碼變得難于理解,而且難于維護。
(28) 請記住,閱讀代碼的時間比寫代碼的時間多得多。思路清晰的設計可獲得易于理解的程序,但注釋、細致的解釋以及一些示例往往具有不可估量的價值。無論對你自 己,還是對后來的人,它們都是相當重要的。如對此仍有懷疑,那么請試想自己試圖從聯機Java文檔里找出有用信息時碰到的挫折,這樣或許能將你說服。
(29) 如認為自己已進行了良好的分析、設計或者實施,那么請稍微更換一下思維角度。試試邀請一些外來人士--并不一定是專家,但可以是來自本公司其他部門的人。 請他們用完全新鮮的眼光考察你的工作,看看是否能找出你一度熟視無睹的問題。采取這種方式,往往能在最適合修改的階段找出一些關鍵性的問題,避免產品發行 后再解決問題而造成的金錢及精力方面的損失。
(30) 良好的設計能帶來最大的回報。簡言之,對于一個特定的問題,通常會花較長的時間才能找到一種最恰當的解決方案。但一旦找到了正確的方法,以后的工作就輕松 多了,再也不用經歷數小時、數天或者數月的痛苦掙扎。我們的努力工作會帶來最大的回報(甚至無可估量)。而且由于自己傾注了大量心血,最終獲得一個出色的 設計方案,成功的快感也是令人心動的。堅持抵制草草完工的誘惑--那樣做往往得不償失
在互聯網這個領域一直有這樣的說法:“如果老二無法戰勝老大,那么就把老大賴以生存的東西開源吧”。當年Yahoo!與Google還是處在 強烈競爭關系時候,招聘了Doug(Hadoop創始人),把Google老大賴以生存的DFS與Map-Reduce開源了,開始了Hadoop的童年 時期。差不多在2008年的時候,Hadoop才算逐漸成熟。 從初創到現在,Hadoop經過了至少7年的積累,現在的Hadoop不僅是當年的老二Yahoo的專用產品了,從Hadoop長長的用戶名單中, 可以看到Facebook、Linkedin、Amazon,可以看到EMC、eBay、Twitter、IBM、Microsoft,、Apple、 HP…國內的公司有淘寶、百度等等。  本文將對Hadoop七年(2004-2011)的發展歷程進 行梳理。讀完本文后,將不難看出,Hadoop的發展基本上經歷了這樣一個過程:從一個開源的Apache基金會項目,隨著越來越多的用戶的加入,不斷地 使用、貢獻和完善,形成一個強大的生態系統,從2009年開始,隨著云計算和大數據的發展,Hadoop作為海量數據分析的最佳解決方案,開始受到許多 IT廠商的關注,從而出現了許多Hadoop的商業版以及支持Hadoop的產品,包括軟件和硬件。 - 2004年,Google發表論文,向全世界介紹了MapReduce。
- 2005年初,為了支持Nutch搜索引擎項目,Nutch的開發者基于Google發布的MapReduce報告,在Nutch上開發了一個可工作的MapReduce應用。
- 2005年年中,所有主要的Nutch算法被移植到使用MapReduce和NDFS(Nutch Distributed File System )來運行。
- 2006年1月,Doug Cutting加入雅虎,Yahoo!提供一個專門的團隊和資源將Hadoop發展成一個可在網絡上運行的系統。
- 2006年2月,Apache Hadoop項目正式啟動以支持MapReduce和HDFS的獨立發展。
- 2007年,百度開始使用Hadoop做離線處理,目前差不多80%的Hadoop集群用作日志處理。
- 2007年,中國移動開始在“大云”研究中使用Hadoop技術,規模超過1000臺。
- 2008年,淘寶開始投入研究基于Hadoop的系統——云梯,并將其用于處理電子商務相關數據。云梯1的總容量大概為9.3PB,包含了1100臺機器,每天處理約18000道作業,掃描500TB數據。
- 2008年1月,Hadoop成為Apache頂級項目。
- 2008年2月,Yahoo!宣布其搜索引擎產品部署在一個擁有1萬個內核的Hadoop集群上。
- 2008年7月,Hadoop打破1TB數據排序基準測試記錄。Yahoo!的一個Hadoop集群用209秒完成1TB數據的排序 ,比上一年的紀錄保持者保持的297秒快了將近90秒。
- 2009 年 3 月,Cloudera推出CDH(Cloudera’s Distribution including Apache Hadoop)平臺,完全由開放源碼軟件組成,目前已經進入第3版。
- 2009年5月,Yahoo的團隊使用Hadoop對1 TB的數據進行排序只花了62秒時間。
- 2009年7月 ,Hadoop Core項目更名為Hadoop Common;
- 2009年7月 ,MapReduce 和 Hadoop Distributed File System (HDFS) 成為Hadoop項目的獨立子項目。
- 2009年7月 ,Avro 和 Chukwa 成為Hadoop新的子項目。
- 2010年5月 ,Avro脫離Hadoop項目,成為Apache頂級項目。
- 2010年5月 ,HBase脫離Hadoop項目,成為Apache頂級項目。
- 2010年5月,IBM提供了基于Hadoop 的大數據分析軟件——InfoSphere BigInsights,包括基礎版和企業版。
- 2010年9月,Hive( Facebook) 脫離Hadoop,成為Apache頂級項目。
- 2010年9月,Pig脫離Hadoop,成為Apache頂級項目。
- 2011年1月,ZooKeeper 脫離Hadoop,成為Apache頂級項目。
- 2011年3月,Apache Hadoop獲得Media Guardian Innovation Awards 。
- 2011年3月, Platform Computing 宣布在它的Symphony軟件中支持Hadoop MapReduce API。
- 2011年5月,Mapr Technologies公司推出分布式文件系統和MapReduce引擎——MapR Distribution for Apache Hadoop。
- 2011年5月,HCatalog 1.0發布。該項目由Hortonworks 在2010年3月份提出,HCatalog主要用于解決數據存儲、元數據的問題,主要解決HDFS的瓶頸,它提供了一個地方來存儲數據的狀態信息,這使得 數據清理和歸檔工具可以很容易的進行處理。
- 2011年4月,SGI( Silicon Graphics International )基于SGI Rackable和CloudRack服務器產品線提供Hadoop優化的解決方案。
- 2011年5月,EMC為客戶推出一種新的基于開源Hadoop解決方案的數據中心設備——GreenPlum HD,以助其滿足客戶日益增長的數據分析需求并加快利用開源數據分析軟件。Greenplum是EMC在2010年7月收購的一家開源數據倉庫公司。
- 2011年5月,在收購了Engenio之后, NetApp推出與Hadoop應用結合的產品E5400存儲系統。
- 2011年6月,Calxeda公司(之前公司的名字是Smooth-Stone)發起了“開拓者行動”,一個由10家軟件公司組成的團隊將為基于Calxeda即將推出的ARM系統上芯片設計的服務器提供支持。并為Hadoop提供低功耗服務器技術。
- 2011年6月,數據集成供應商Informatica發布了其旗艦產品,產品設計初衷是處理當今事務和社會媒體所產生的海量數據,同時支持Hadoop。
- 2011年7月,Yahoo!和硅谷風險投資公司 Benchmark Capital創建了Hortonworks 公司,旨在讓Hadoop更加魯棒(可靠),并讓企業用戶更容易安裝、管理和使用Hadoop。
- 2011年8月,Cloudera公布了一項有益于合作伙伴生態系統的計劃——創建一個生態系統,以便硬件供應商、軟件供應商以及系統集成商可以一起探索如何使用Hadoop更好的洞察數據。
- 2011年8月,Dell與Cloudera聯合推出Hadoop解決方案——Cloudera Enterprise。Cloudera Enterprise基于Dell PowerEdge C2100機架服務器以及Dell PowerConnect 6248以太網交換機 。
 在梳理的過程中,筆者發現了上圖,它很好地展現了Hadoop生態系統是如何在使用中一步一步成長起來的。
本文轉自:http://linuxtoy.org/archives/bash-shortcuts.html
生活在 Bash shell 中,熟記以下快捷鍵,將極大的提高你的命令行操作效率。 編輯命令 - Ctrl + a :移到命令行首
- Ctrl + e :移到命令行尾
- Ctrl + f :按字符前移(右向)
- Ctrl + b :按字符后移(左向)
- Alt + f :按單詞前移(右向)
- Alt + b :按單詞后移(左向)
- Ctrl + xx:在命令行首和光標之間移動
- Ctrl + u :從光標處刪除至命令行首
- Ctrl + k :從光標處刪除至命令行尾
- Ctrl + w :從光標處刪除至字首
- Alt + d :從光標處刪除至字尾
- Ctrl + d :刪除光標處的字符
- Ctrl + h :刪除光標前的字符
- Ctrl + y :粘貼至光標后
- Alt + c :從光標處更改為首字母大寫的單詞
- Alt + u :從光標處更改為全部大寫的單詞
- Alt + l :從光標處更改為全部小寫的單詞
- Ctrl + t :交換光標處和之前的字符
- Alt + t :交換光標處和之前的單詞
- Alt + Backspace:與 Ctrl + w 相同類似,分隔符有些差別 [感謝 rezilla 指正]
重新執行命令 - Ctrl + r:逆向搜索命令歷史
- Ctrl + g:從歷史搜索模式退出
- Ctrl + p:歷史中的上一條命令
- Ctrl + n:歷史中的下一條命令
- Alt + .:使用上一條命令的最后一個參數
控制命令 - Ctrl + l:清屏
- Ctrl + o:執行當前命令,并選擇上一條命令
- Ctrl + s:阻止屏幕輸出
- Ctrl + q:允許屏幕輸出
- Ctrl + c:終止命令
- Ctrl + z:掛起命令
Bang (!) 命令 - !!:執行上一條命令
- !blah:執行最近的以 blah 開頭的命令,如 !ls
- !blah:p:僅打印輸出,而不執行
- !$:上一條命令的最后一個參數,與 Alt + . 相同
- !$:p:打印輸出 !$ 的內容
- !*:上一條命令的所有參數
- !*:p:打印輸出 !* 的內容
- ^blah:刪除上一條命令中的 blah
- ^blah^foo:將上一條命令中的 blah 替換為 foo
- ^blah^foo^:將上一條命令中所有的 blah 都替換為 foo
友情提示: - 以上介紹的大多數 Bash 快捷鍵僅當在 emacs 編輯模式時有效,若你將 Bash 配置為 vi 編輯模式,那將遵循 vi 的按鍵綁定。Bash 默認為 emacs 編輯模式。如果你的 Bash 不在 emacs 編輯模式,可通過
set -o emacs 設置。 - ^S、^Q、^C、^Z 是由終端設備處理的,可用
stty 命令設置。
本文轉自 http://trinea.iteye.com/blog/1196400
1、jps的作用 jps類似linux的ps命令,不同的是ps是用來顯示進程,而jps只顯示java進程,準確的說是當前用戶已啟動的部分java進程信息,信息包括進程號和簡短的進程command。 2、某個java進程已經啟動,用jps卻顯示不了該進程進程號 這個問題已經碰到過兩次了,所以在這里總結下。 現象: 用ps -ef|grep java能看到啟動的java進程,但是用jps查看卻不存在該進程的id。待會兒解釋過之后就能知道在該情況下,jconsole、jvisualvm可能無法監控該進程,其他java自帶工具也可能無法使用 分析: java程序啟動后,默認(請注意是默認)會在/tmp/hsperfdata_userName目錄下以該進程的id為文件名新建文件,并在該文件中存儲jvm運行的相關信息,其中的userName為當前的用戶名,/tmp/hsperfdata_userName目錄會存放該用戶所有已經啟動的java進程信息。對于windows機器/tmp用Windows存放臨時文件目錄代替。 而jps、jconsole、jvisualvm等工具的數據來源就是這個文件(/tmp/hsperfdata_userName/pid)。所以當該文件不存在或是無法讀取時就會出現jps無法查看該進程號,jconsole無法監控等問題 原因: (1)、磁盤讀寫、目錄權限問題 若該用戶沒有權限寫/tmp目錄或是磁盤已滿,則無法創建/tmp/hsperfdata_userName/pid文件。或該文件已經生成,但用戶沒有讀權限 (2)、臨時文件丟失,被刪除或是定期清理 對于linux機器,一般都會存在定時任務對臨時文件夾進行清理,導致/tmp目錄被清空。這也是我第一次碰到該現象的原因 這個導致的現象可能會是這樣,用jconsole監控進程,發現在某一時段后進程仍然存在,但是卻沒有監控信息了。 (3)、java進程信息文件存儲地址被設置,不在/tmp目錄下 上面我們在介紹時說默認會在/tmp/hsperfdata_userName目錄保存進程信息,但由于以上1、2所述原因,可能導致該文件無法生成或是丟失,所以java啟動時提供了參數,可以對這個文件的位置進行設置,而jps、jconsole都只會從/tmp目錄讀取,而無法從設置后的目錄讀物信息, 這個問題只會在jdk 6u23和6u24上出現,在6u23和6u24上,進程信息會保存在-Djava.io.tmpdir下, 因此如果它被設置為非/tmp目錄則會導致 jps,jconsole等無法讀取的現象, 但在其他版本的jdk上,即使設置-Djava.io.tmpdir為非/tmp, 也會在/tmp/hsperfdata_userName下保存java進程信息.因此可以說這是6u23和6u24的bug, 以下是jdk對該bug的描述地址:
bug描述: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7021676 bug的修復描述: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7009828 bug修改代碼: http://hg.openjdk.java.net/jdk7/hotspot/hotspot/rev/34d64ad817f4
關于設置該文件位置的參數為-Djava.io.tmpdir
1. 根據上一章配好的集群,現為Myhost1配置backupNode和SecondaryNamenode, 由于機器有限,這里就不為Myhost2配置backupNode和SecondaryNamenode,但是方法相同. 2. 我們選定Myhost4為SecondaryNamenode, Myhost5為backupNode. 配置并啟動SecondaryNamenode: 1. 配置:為Myhost1的 hdfs-site.xml 加入如下配置,指定SecondaryNamenode. <property> <name>dfs.namenode.secondary.http-address</name> <value> Myhost4:9001</value> </property> 2. Myhost4的hdfs-site.xml 加入如下配置,指定nn的url和本地的checkpoint.dir. <property> <name>dfs.federation.nameservice.id</name> <value> Myhost1:50070</value> </property> <property> <name>dfs.namenode.checkpoint.dir</name> <value>/home/yuling.sh/checkpoint-data</value> </property> 3. 啟動SecondaryNamenode. 在Myhost1上運行命令:sbin/star-dfs.sh或者在Myhost4上運行sbin/hadoop-daemo.sh start SecondaryNamenode 即可以啟動SecondaryNamenode. 可以通過log或者網頁Myhost4:50090查看其狀態. 另外在checkpoint.dir下會有元數據信息. 配置并啟動backupNode: 1. 配置Myhost5的hdfs-site.xml, 加入如下配置信息: <property> <name>dfs.namenode.backup.address</name> <value> Myhost5:9002</value> </property> <property> <name>dfs.namenode.backup.http-address</name> <value> Myhost5:9003</value> </property> <property> <name>dfs.namenode.http-address</name> <value> Myhost1:50070</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/home/yuling.sh/ backup-data</value> </property> 2. 啟動backupNode, 在Myhost5上運行bin/hdfs namenode –backup & 3. 在dfs.namenode.name.dir下查看元數據信息.
下一篇博客將講述如何搭建hadoop 0.23 的mapreduce
使用hadoop-0.23 搭建hdfs, namenode + datanode 1. HDFS-1052引入了多namenode, HDFS架構變化較大, 可以參考hortonworks的文章: http://hortonworks.com/an-introduction-to-hdfs-federation/. 我將在接下來的博客里把此文章翻譯一下(另外還有: http://developer.yahoo.com/blogs/hadoop/posts/2011/03/mapreduce-nextgen-scheduler/). 所有namenode共享datanode, 各個namenode相互獨立, 互不影響, 每個namenode都有一個backupNode和SecondaryNamenode,提供主備切換功能和備份元數據的功能. 下文的配置信息主要參考HDFS-2471. 2. 環境: a) 五臺機器 ,linux系統, b) 互相添加ssh-key,后應該可以不用密碼互連 c) 編譯好的0.23版本的包: hadoop-0.23.0-SNAPSHOT.tar.gz d) 每臺機器需要安裝java1.6或以上版本.并把JAVA_HOME加到$PATH中. e) 最好加上pssh和pscp工具. 這里把五臺機器命名為: Myhost1 Myhost2 Myhost3 Myhost4 Myhost5 假設我們需要搭建如下集群: Myhost1和Myhost2開啟 namenode, 另外三臺機器啟動datanode服務. 3. 首先把分配到五臺機器上,然后解壓.(推薦使用pscp, pssh命令) 4. 然后在五臺機器上安裝java,并把JAVA_HOME加到$PATH中 5. 進入解壓后的hadoop目錄, 編輯 etc/hadoop/hdfs-site.xml a) Myhost1的配置如下(其中hadoop存放在/home/yuling.sh/目錄下): <property> <name>fs.defaultFS</name> <value>hdfs:// Myhost1:9000</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/home/yuling.sh/cluster-data</value> </property> b) Myhost2的配置如下(其中hadoop存放在/home/yuling.sh/目錄下): <property> <name>fs.defaultFS</name> <value>hdfs:// Myhost2:9000</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/home/yuling.sh/cluster-data</value> </property> c) 這里把Myhost1集群起名ns1, Myhost1集群起名ns2, 三臺slava的etc/hadoop/hdfs-site.xml配置如下: <property> <name>dfs.federation.nameservices</name> <value>ns1,ns2</value> </property> <property> <name>dfs.namenode.rpc-address.ns1</name> <value>hdfs:// Myhost1:9000</value> </property> <property> <name>dfs.namenode.http-address.ns1</name> <value> Myhost1:50070</value> </property> <property> <name>dfs.namenode.rpc-address.ns2</name> <value>hdfs:// Myhost2:9000</value> </property> <property> <name>dfs.namenode.http-address.ns1</name> <value> Myhost2:50070</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/home/yuling.sh/datanode</value> </property> d) 解釋:namenode需要指定兩個參數, 用于存放元數據和文件系統的URL. Datanode需指定要連接的namenode 的rpc-address和http-address. 以及數據存放位置dfs.datanode.data.dir. 6. 然后編輯兩臺namenode的hadoop目錄下 etc/hadoop/slaves文件. 加入三臺slave機器名: Myhost3 Myhost4 Myhost5 7. 現在需要格式化namenode, 由于namenode共享datanode, 因此它們的clusterid需要有相同的名字.這里我們把名字設為 yuling .命令如下: bin/hdfs namenode –format –clusterid yuling 兩臺機器格式話之后會在/home/yuling.sh/cluster-data下生成元數據目錄. 8. 啟動Myhost1和Myhost2上的namenode和slave上datanode服務. 命令如下: sbin/start-hdfs.sh 分別在Myhost1和Myhost2下運行. 9. 啟動之后打開瀏覽器, 分別查看兩namenode啟動后狀態. URL為: Myhost1:50070和Myhost2:50070 10. 這期間可能會遇到許多問題, 但是可以根據拋出的異常自己解決, 我這里就不多說了. 下一篇博客將講述如何啟動backupNode和SecondaryNamenode
編譯(環境linux, 需要聯網) 1. 首先下載hadoop 0.23版本 svn checkout http://svn.apache.org/repos/asf/hadoop/common/tags/release-0.23.0-rc0/ 2. 進入release-0.23.0-rc0目錄下能看到INSTALL.TXT文件, 這里有編譯hadoop 0.23的教程. 編譯前的準備:. a) * Unix System b) * JDK 1.6 c) * Maven 3.0 d) * Forrest 0.8 (if generating docs) e) * Findbugs 1.3.9 (if running findbugs) f) * ProtocolBuffer 2.4.1+ (for MapReduce) g) * Autotools (if compiling native code) h) * Internet connection for first build (to fetch all Maven and Hadoop dependencies) 可以根據需要安裝全部或部分的工具,然后把它們加入到$PATH中. 這里介紹一下ProtocolBuffer的安裝方法:下載2.4.1版本后解壓,進入目錄,運行如下命令即可. $ ./configure --prefile=/usr/local $ make $ sudo make install 3. 經過第二步準備之后,由于從hadoop0.23開始使用Maven編譯,因此必需聯網,命令如下: mvn package [-Pdist][-Pdocs][-Psrc][-Pnative][-Dtar] 建議先運行命令: mvn package -Pdist -DskipTests –Dtar (前提Maven 3.0和ProtocolBuffer2.4.1以上), 此命令成功之后會在release-0.23.0-rc0/下生成 hadoop-dist/target/hadoop-0.23.0-SNAPSHOT.tar.gz. 可以使用這個包搭建集群. 使用-Pdocs選項可以生成文檔,當然前提是安裝了Forrest 0.8和Findbugs 1.3.9. 可以參考如下命令手動指定:FORREST_HOME和FINDBUGS_HOME. mvn package -Pdocs -DskipTests -Dtar -Dmaven.test.skip -Denv.FORREST_HOME=/usr/local/apache-forrest-0.9 -Denv.FINDBUGS_HOME=/usr/local/findbugs-1.3.9 生成的文檔在各自的target/site目錄下.
經過以上步驟,我們已經編譯好了hadoop-0.23,現在可以使用hadoop-0.23.0-SNAPSHOT.tar.gz來搭建集群了. 下一篇博客將講述如何使用hadoop-0.23 搭建hdfs集群
本周末學習zookeeper,原理和安裝配置 本文參考: http://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/ http://zookeeper.apache.org/ Zookeeper 作為一個分布式的服務框架,主要用來解決分布式集群中應用系統的一致性問題,它能提供基于類似于文件系統的目錄節點樹方式的數據存儲,但是 Zookeeper 并不是用來專門存儲數據的,它的作用主要是用來維護和監控你存儲的數據的狀態變化。通過監控這些數據狀態的變化,從而可以達到基于數據的集群管理。 Zookeeper安裝和配置比較簡單,可以參考官網. 數據模型 Zookeeper 會維護一個具有層次關系的數據結構,它非常類似于一個標準的文件系統,如圖 1 所示: 圖 1 Zookeeper 數據結構

Zookeeper 這種數據結構有如下這些特點: - 每個子目錄項如 NameService 都被稱作為 znode,這個 znode 是被它所在的路徑唯一標識,如 Server1 這個 znode 的標識為 /NameService/Server1
- znode 可以有子節點目錄,并且每個 znode 可以存儲數據,注意 EPHEMERAL 類型的目錄節點不能有子節點目錄
- znode 是有版本的,每個 znode 中存儲的數據可以有多個版本,也就是一個訪問路徑中可以存儲多份數據
- znode 可以是臨時節點,一旦創建這個 znode 的客戶端與服務器失去聯系,這個 znode 也將自動刪除,Zookeeper 的客戶端和服務器通信采用長連接方式,每個客戶端和服務器通過心跳來保持連接,這個連接狀態稱為 session,如果 znode 是臨時節點,這個 session 失效,znode 也就刪除了
- znode 的目錄名可以自動編號,如 App1 已經存在,再創建的話,將會自動命名為 App2
- znode 可以被監控,包括這個目錄節點中存儲的數據的修改,子節點目錄的變化等,一旦變化可以通知設置監控的客戶端,這個是 Zookeeper 的核心特性,Zookeeper 的很多功能都是基于這個特性實現的,后面在典型的應用場景中會有實例介紹
ZooKeeper 典型的應用場景 Zookeeper 從設計模式角度來看,是一個基于觀察者模式設計的分布式服務管理框架,它負責存儲和管理大家都關心的數據,然后接受觀察者的注冊,一旦這些數據的狀態發生 變化,Zookeeper 就將負責通知已經在 Zookeeper 上注冊的那些觀察者做出相應的反應,從而實現集群中類似 Master/Slave 管理模式,關于 Zookeeper 的詳細架構等內部細節可以閱讀 Zookeeper 的源碼 下面詳細介紹這些典型的應用場景,也就是 Zookeeper 到底能幫我們解決那些問題?下面將給出答案。 統一命名服務(Name Service) 分布式應用中,通常需要有一套完整的命名規則,既能夠產生唯一的名稱又便于人識別和記住,通常情況下用樹形的名稱結構是一個理想的選擇,樹形 的名稱結構是一個有層次的目錄結構,既對人友好又不會重復。說到這里你可能想到了 JNDI(Java Naming and Directory Interface,Java命名和目錄接口,是一組在Java應用中訪問命名和目錄服務的API),沒錯 Zookeeper 的 Name Service 與 JNDI 能夠完成的功能是差不多的,它們都是將有層次的目錄結構關聯到一定資源上,但是 Zookeeper 的 Name Service 更加是廣泛意義上的關聯,也許你并不需要將名稱關聯到特定資源上,你可能只需要一個不會重復名稱,就像數據庫中產生一個唯一的數字主鍵一樣。 Name Service 已經是 Zookeeper 內置的功能,你只要調用 Zookeeper 的 API 就能實現。如調用 create 接口就可以很容易創建一個目錄節點。 配置管理(Configuration Management) 配置的管理在分布式應用環境中很常見,例如同一個應用系統需要多臺 PC Server 運行,但是它們運行的應用系統的某些配置項是相同的,如果要修改這些相同的配置項,那么就必須同時修改每臺運行這個應用系統的 PC Server,這樣非常麻煩而且容易出錯。 像這樣的配置信息完全可以交給 Zookeeper 來管理,將配置信息保存在 Zookeeper 的某個目錄節點中,然后將所有需要修改的應用機器監控配置信息的狀態,一旦配置信息發生變化,每臺應用機器就會收到 Zookeeper 的通知,然后從 Zookeeper 獲取新的配置信息應用到系統中。 圖 2. 配置管理結構圖
集群管理(Group Membership) Zookeeper 能夠很容易的實現集群管理的功能,如有多臺 Server 組成一個服務集群,那么必須要一個“總管”知道當前集群中每臺機器的服務狀態,一旦有機器不能提供服務,集群中其它集群必須知道,從而做出調整重新分配服 務策略。同樣當增加集群的服務能力時,就會增加一臺或多臺 Server,同樣也必須讓“總管”知道。 Zookeeper 不僅能夠幫你維護當前的集群中機器的服務狀態,而且能夠幫你選出一個“總管”,讓這個總管來管理集群,這就是 Zookeeper 的另一個功能 Leader Election。 它們的實現方式都是在 Zookeeper 上創建一個 EPHEMERAL 類型的目錄節點,然后每個 Server 在它們創建目錄節點的父目錄節點上調用 getChildren(String path, boolean watch) 方法并設置 watch 為 true,由于是 EPHEMERAL 目錄節點,當創建它的 Server 死去,這個目錄節點也隨之被刪除,所以 Children 將會變化,這時 getChildren上的 Watch 將會被調用,所以其它 Server 就知道已經有某臺 Server 死去了。新增 Server 也是同樣的原理。 Zookeeper 如何實現 Leader Election,也就是選出一個 Master Server。和前面的一樣每臺 Server 創建一個 EPHEMERAL 目錄節點,不同的是它還是一個 SEQUENTIAL 目錄節點,所以它是個 EPHEMERAL_SEQUENTIAL 目錄節點。之所以它是 EPHEMERAL_SEQUENTIAL 目錄節點,是因為我們可以給每臺 Server 編號,我們可以選擇當前是最小編號的 Server 為 Master,假如這個最小編號的 Server 死去,由于是 EPHEMERAL 節點,死去的 Server 對應的節點也被刪除,所以當前的節點列表中又出現一個最小編號的節點,我們就選擇這個節點為當前 Master。這樣就實現了動態選擇 Master,避免了傳統意義上單 Master 容易出現單點故障的問題。 圖 3. 集群管理結構圖
這部分的示例代碼如下,完整的代碼請看源代碼: 清單 3. Leader Election 關鍵代碼 void findLeader() throws InterruptedException { byte[] leader = null; try { leader = zk.getData(root + "/leader", true, null); } catch (Exception e) { logger.error(e); } if (leader != null) { following(); } else { String newLeader = null; try { byte[] localhost = InetAddress.getLocalHost().getAddress(); newLeader = zk.create(root + "/leader", localhost, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } catch (Exception e) { logger.error(e); } if (newLeader != null) { leading(); } else { mutex.wait(); } } } | 共享鎖(Locks) 共享鎖在同一個進程中很容易實現,但是在跨進程或者在不同 Server 之間就不好實現了。Zookeeper 卻很容易實現這個功能,實現方式也是需要獲得鎖的 Server 創建一個 EPHEMERAL_SEQUENTIAL 目錄節點,然后調用 getChildren方法獲取當前的目錄節點列表中最小的目錄節點是不是就是自己創建的目錄節點,如果正是自己創建的,那么它就獲得了這個鎖,如果不是那么它就調用 exists(String path, boolean watch) 方法并監控 Zookeeper 上目錄節點列表的變化,一直到自己創建的節點是列表中最小編號的目錄節點,從而獲得鎖,釋放鎖很簡單,只要刪除前面它自己所創建的目錄節點就行了。 圖 4. Zookeeper 實現 Locks 的流程圖
同步鎖的實現代碼如下,完整的代碼請看源代碼: 清單 4. 同步鎖的關鍵代碼 void getLock() throws KeeperException, InterruptedException{ List<String> list = zk.getChildren(root, false); String[] nodes = list.toArray(new String[list.size()]); Arrays.sort(nodes); if(myZnode.equals(root+"/"+nodes[0])){ doAction(); } else{ waitForLock(nodes[0]); } } void waitForLock(String lower) throws InterruptedException, KeeperException { Stat stat = zk.exists(root + "/" + lower,true); if(stat != null){ mutex.wait(); } else{ getLock(); } } | 隊列管理 Zookeeper 可以處理兩種類型的隊列: - 當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達,這種是同步隊列。
- 隊列按照 FIFO 方式進行入隊和出隊操作,例如實現生產者和消費者模型。
同步隊列用 Zookeeper 實現的實現思路如下: 創建一個父目錄 /synchronizing,每個成員都監控標志(Set Watch)位目錄 /synchronizing/start 是否存在,然后每個成員都加入這個隊列,加入隊列的方式就是創建 /synchronizing/member_i 的臨時目錄節點,然后每個成員獲取 / synchronizing 目錄的所有目錄節點,也就是 member_i。判斷 i 的值是否已經是成員的個數,如果小于成員個數等待 /synchronizing/start 的出現,如果已經相等就創建 /synchronizing/start。 用下面的流程圖更容易理解: 圖 5. 同步隊列流程圖
同步隊列的關鍵代碼如下,完整的代碼請看附件: 清單 5. 同步隊列 void addQueue() throws KeeperException, InterruptedException{ zk.exists(root + "/start",true); zk.create(root + "/" + name, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); synchronized (mutex) { List<String> list = zk.getChildren(root, false); if (list.size() < size) { mutex.wait(); } else { zk.create(root + "/start", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } } | 當隊列沒滿是進入 wait(),然后會一直等待 Watch 的通知,Watch 的代碼如下: public void process(WatchedEvent event) { if(event.getPath().equals(root + "/start") && event.getType() == Event.EventType.NodeCreated){ System.out.println("得到通知"); super.process(event); doAction(); } } | FIFO 隊列用 Zookeeper 實現思路如下: 實現的思路也非常簡單,就是在特定的目錄下創建 SEQUENTIAL 類型的子目錄 /queue_i,這樣就能保證所有成員加入隊列時都是有編號的,出隊列時通過 getChildren( ) 方法可以返回當前所有的隊列中的元素,然后消費其中最小的一個,這樣就能保證 FIFO。 下面是生產者和消費者這種隊列形式的示例代碼,完整的代碼請看附件: 清單 6. 生產者代碼 boolean produce(int i) throws KeeperException, InterruptedException{ ByteBuffer b = ByteBuffer.allocate(4); byte[] value; b.putInt(i); value = b.array(); zk.create(root + "/element", value, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); return true; } | 清單 7. 消費者代碼 int consume() throws KeeperException, InterruptedException{ int retvalue = -1; Stat stat = null; while (true) { synchronized (mutex) { List<String> list = zk.getChildren(root, true); if (list.size() == 0) { mutex.wait(); } else { Integer min = new Integer(list.get(0).substring(7)); for(String s : list){ Integer tempValue = new Integer(s.substring(7)); if(tempValue < min) min = tempValue; } byte[] b = zk.getData(root + "/element" + min,false, stat); zk.delete(root + "/element" + min, 0); ByteBuffer buffer = ByteBuffer.wrap(b); retvalue = buffer.getInt(); return retvalue; } } } } | 總結 Zookeeper 作為 Hadoop 項目中的一個子項目,是 Hadoop 集群管理的一個必不可少的模塊,它主要用來控制集群中的數據,如它管理 Hadoop 集群中的 NameNode,還有 Hbase 中 Master Election、Server 之間狀態同步等。 本文介紹的 Zookeeper 的基本知識,以及介紹了幾個典型的應用場景。這些都是 Zookeeper 的基本功能,最重要的是 Zoopkeeper 提供了一套很好的分布式集群管理的機制,就是它這種基于層次型的目錄樹的數據結構,并對樹中的節點進行有效管理,從而可以設計出多種多樣的分布式的數據管 理模型,而不僅僅局限于上面提到的幾個常用應用場景。
使用 Linux 系統總是免不了要接觸包管理工具。比如,Debian/Ubuntu 的 apt、openSUSE 的 zypp、Fedora 的 yum、Mandriva 的 urpmi、Slackware 的 slackpkg、Archlinux 的 pacman、Gentoo 的 emerge、Foresight 的 conary、Pardus 的 pisi,等等。DistroWatch 針對上述包管理器的主要用法進行了總結,對各位 Linux 用戶來說具有很好的參考作用。這個總結還是有一點不足,有空給大家整理一個更全面的版本。 任務 | apt Debian, Ubuntu | zypp openSUSE | yum Fedora, CentOS | 安裝包 | apt-get install <pkg> | zypper install <pkg> | yum install <pkg> | 移除包 | apt-get remove <pkg> | zypper remove <pkg> | yum erase <pkg> | 更新包列表 | apt-get update | zypper refresh | yum check-update | 更新系統 | apt-get upgrade | zypper update | yum update | 列出源 | cat /etc/apt/sources.list | zypper repos | yum repolist | 添加源 | (edit /etc/apt/sources.list) | zypper addrepo <path> <name> | (add <repo> to /etc/yum.repos.d/) | 移除源 | (edit /etc/apt/sources.list) | zypper removerepo <name> | (remove <repo> from /etc/yum.repos.d/) | 搜索包 | apt-cache search <pkg> | zypper search <pkg> | yum search <pkg> | 列出已安裝的包 | dpkg -l | rpm -qa | rpm -qa | 任務 | urpmi Mandriva | slackpkg Slackware | pacman Arch | 安裝包 | urpmi <pkg> | slackpkg install <pkg> | pacman -S <pkg> | 移除包 | urpme <pkg> | slackpkg remove <pkg> | pacman -R <pkg> | 更新包列表 | urpmi.update -a | slackpkg update | pacman -Sy | 更新系統 | urpmi --auto-select | slackpkg upgrade-all | pacman -Su | 列出源 | urpmq --list-media | cat /etc/slackpkg/mirrors | cat /etc/pacman.conf | 添加源 | urpmi.addmedia <name> <path> | (edit /etc/slackpkg/mirrors) | (edit /etc/pacman.conf) | 移除源 | urpmi.removemedia <media> | (edit /etc/slackpkg/mirrors) | (edit /etc/pacman.conf) | 搜索包 | urpmf <pkg> | -- | pacman -Qs <pkg> | 列出已安裝的包 | rpm -qa | ls /var/log/packages/ | pacman -Qii | 任務 | conary rPath, Foresight | pisi Pardus | emerge Gentoo | 安裝包 | conary update <pkg> | pisi install <pkg> | emerge <pkg> | 移除包 | conary erase <pkg> | pisi remove <pkg> | emerge -C <pkg> | 更新包列表 | | pisi update-repo | emerge --sync | layman -S [for added repositories] | 更新系統 | conary updateall | pisi upgrade | emerge -NuDa world | 列出源 | | pisi list-repo | layman -L | 添加源 | | pisi add-repo <name> <path> | layman -a | 移除源 | | pisi remove-repo <name> | layman -d | 搜索包 | conary query <pkg> | pisi search <pkg> | emerge --search | 列出已安裝的包 | conary query | pisi list-installed | cat /var/lib/portage | more |
本文轉自 http://linuxtoy.org/archives/linux-package-management-cheatsheet.html
-Xms256m -Xmx512m -Xmn128m -XX:PermSize=96m -XX:MaxPermSize=96m -Xverify:none -Xnoclassgc -XX:UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=85 加入以上參數能使eclipse啟動速度得到加快.至于各個參數的意義可以在網上查找到,這里不詳細贅述.
Eclipse插件開發 1. 下載并安裝jdk和eclipse 這里強調一下: 需要下載Eclipse for RCP and RAP Developers, 否則無法新建Plug-in Development 項目. 2. 新建項目 安裝好之后打開eclipse, 點擊 File->NewProject。選擇Plug-in Project,點擊Next。新建一個名為com.developer.showtime的項目,所有參數采用默認值. 3. 在com.developer.showtime項目的src下新建一個類: ShowTime,代碼如下:
package com.developer.showtime;
import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IStartup;
public class ShowTime implements IStartup { public void earlyStartup() { Display.getDefault().syncExec(new Runnable() { public void run(){ long eclipseStartTime = Long.parseLong(System.getProperty("eclipse.startTime")); long costTime = System.currentTimeMillis() - eclipseStartTime; Shell shell = Display.getDefault().getActiveShell(); String message = "Eclipse start in " + costTime + "ms"; MessageDialog.openInformation(shell, "Information", message); } }); } }
4. 修改plugin.xml文件如下: <?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin> <extension
point="org.eclipse.ui.startup">
<startup class="com.developer.showtime.ShowTime"/>
</extension>
</plugin> 5. 試運行 右鍵點擊Run as -> Eclipse Application. 此時會運行一個eclipse, 啟動之后就能顯示啟動所需時間. 6. 導出插件. 右鍵Export -> Deployable plug-ins and fragments. 在Directory中輸入需要導出的路徑, 點擊finish后會在該目錄下產生一個plugins的目錄, 里面就是插件包: com.developer.showTime_1.0.0.201110161216.jar. 把這個包復制到eclipse目錄下的plugin目錄下. 然后再啟動eclipse 便可以看到eclipse啟動所花的時間.
本周學習了mapreduce-64,對map端的spill有了較為深入的了解. 附件描述了修改前后sort的原理.mapreduce-64前spill原理較為簡單,打上mapreduce-64后主要流程也不難,需要了解各個參數的意義.
下文翻譯自yahoo博客:http://developer.yahoo.com/blogs/hadoop/posts/2011/02/mapreduce-nextgen/
Hadoop的下一代mapreduce 概述 在大數據商業領域中,運行個數少但較大的集群比運行多個小集群更劃算,大集群還可以處理更大的數據集并支持更多的作業和用戶. Apache Hadoop 的MapReduce框架已經達到4000臺機器的擴展極限,我們正在發展下一代MapReduce,使其成為一個通用資源管理,單作業,用戶自定義組件,管理著應用程序執行的框架. 由于停機成本更大,高可用必需從一開始就得建立,就如安全性和多用戶組,用以支持更多用戶使用更大的集群,新的構架在許多地方進行了創新,增加了敏捷性和機器利用率. 背景 當前Apache Hadoop 的MapReduce的接口會顯示其年齡. 由于集群大小和工作負載的變化趨勢, MapReduce的JobTracker需要徹底的改革以解決其可擴展性,內存消耗,線程模型,可靠性和性能上的不足. 過去五年,我們做了一些小的修復,然而最近,修改框架的的成本越來越高. 結構的缺陷和糾正措施都很好理解,甚至早在2007年,當我們記錄下修復建議: https://issues.apache.org/jira/browse/MAPREDUCE-278. 從運營的角度看,目前的Hadoop MapReduce框架面臨系統級別的升級,以解決例如bug修復,性能改善和功能的需求. 更糟糕的是,它迫使每個用戶也需要同時升級,不顧其利益;這使用戶使用新版本的周期變長. 需求 我們考慮改善Hadoop MapReduce框架的方法,重要的是記住最迫切的需求,下一代Hadoop MapReduce框架最迫切的需求是: - 可靠性
- 可用性
- 可擴展性 - 10000臺機器,200000核,或者更多
- 向后兼容性 - 確保用戶的MapReduce應用程序在下一代框架下不需要改變
- 進展 – 客戶端可以控制hadoop軟件堆棧的升級.
- 可預測的延遲 – 用戶很關注的一點.
- 集群利用率
第二層次需求: - 使MapReduce支持備用編程范式
- 支持短時間的服務
鑒于以上需求,顯然我們需要重新考慮使用hadoop成為數據處理的基礎設施. 事實上,當前MapReduce結構無法滿足我們的需求,因此需要新的創新,這在hadoop社區這已成為共識,查看2008年一月的一個提議,在jira: https://issues.apache.org/jira/browse/MAPREDUCE-279. 下一代MapReduce 重構的基本思想是把jobtracker的兩大功能分開,使資源管理和作業分配/監控成為兩個部件.新的資源管理器管理提供給應用(一個或多個)的計算資源,應用管理中心管理應用程序的調度和協調,應用程序既是一個經典MapReduce作業也是這類作業的DAG. 資源管理器和每臺機器的NodeManager服務,管理該機上的用戶進程,形成計算結構. 每個應用程序的ApplicationMaster是一個具體庫的架構,負責從資源管理器請求資源,并和NodeManager協同執行和監控任務. 資源管理器支持應用程序的分組,這些組保證使用一定比例集群資源. 它是純粹的調度,也就是,它運行時并不監控和追蹤應用的狀態. 此外,它不保證重新啟動失敗的任務,無論是應用程序或硬件導致的失敗. 資源管理器執行調度功能是基于應用的資源需求,每個應用需要多種資源需求,代表對對容器所需的資源,資源需求包括內存,cpu,硬盤,網絡等,注意這與當前使用slot模型的MapReduce有很大的不同,slot模型導致集群利用率不高,資源管理器有一個調度策略插件,負責分把集群資源分給各個組,應用等.有基礎的調度插件,例如:當前的CapacityScheduler 和FairScheduler. NodeManager是每臺機器的框架代理,負責提交應用程序的容器,監控他們的資源利用率(cpu,內存,硬盤,網絡),并且報告給調度器. 每個應用程序的ApplicationMaster負責與調度器請求適當的資源容器,提交作業,追蹤其狀態,監控進度和處理失敗任務. 結構 改進當前實現面對面的Hadoop MapReduce 可擴展性 在集群中把資源管理從集群管理器的整個生命周期和他們的部件中分離出來后形成的架構:擴展性更好并且更優雅, Hadoop MapReduce的JobTracker花費很大一部分時間和精力管理應用程序的生命周期,這是導致軟件災難的原因.把它移到應用指定的實體是一個重大的勝利. 可擴展性在當前硬件趨勢下更加重要,當前hadoop的MapReduce已經發展到4000臺機器,然而4000臺機器在2009年(例:8core,16G RAM,4TB硬盤)只有2011年400臺機器的一半(16core,48G RAM, 24TB硬盤). 并且,運營成本的因素有助于迫使和鞏固我們使用更大的集群:6000臺機器或者更多. 可用性 - 資源管理器 – 使用 Apache ZooKeeper 用于故障轉移. 當資源管理器發生故障,另外一個可以迅速恢復,這是由于集群狀態保存在ZooKeeper中. 資源管理器失敗后,重啟所有組和正在運行的應用程序.
- 應用中心 - 下一代MapReduce支持應用特殊點的檢查功能 ,依靠其把自身狀態存儲在hdfs上的功能,MapReduce 應用中心可以從失敗中恢復,
兼容性 下一代MapReduce使用線兼容協議以允許不同版本的服務端和客戶端相互通信,在將來的releases版本,這將使集群滾動升級,一個重要的可操作性便成功了. 創新和敏捷性 提出的構架一個主要優點是MapReduce將更有效,成為user-land library. 計算框架(資源管理器和節點管理器)完全通用并在MapReduce看來是透明的. 這使最終客戶在同一個集群使用可用不同版本的MapReduce, 這是微不足道的支持,因為MapReduce的應用中心和運行時的多版本可用于不同的應用. 這為應用提供顯著的靈活性,因為整個集群沒必要升級,如修復bug,改進和新功能的應用. 它也允許終端用戶根據他們自己的安排升級其應用到MapReduce版本,這大大提高了集群的可操作性. 允許用戶自定義的Map-Reduce版本的創新不會影響軟件的穩定性. 這是微不足道的,就像hadoop在線原型進入用戶MapReduce版本而不影響其他用戶.( It will be trivial to incorporate features such as the Hadoop Online Prototype into the user’s version of MapReduce without affecting other users.) 集群利用率 下一代MapReduce資源管理器使用通用概念,用于調度和分配給單獨的個體. 集群中的每個機器資源是概念性的,例如內存,cpu,I/O帶寬等. 每個機器都是可替代的,分配給應用程序就像基于應用指定需求資源的容器.每個容器包括一些處理器,并和其他容器邏輯隔離,提供強有利的多租戶支持. 它刪除了當前hadoop MapReduce中map和reduce slots概念. Slot會影響集群的利用率,因為在任何時候,無論map和reduce都是稀缺的. 支持MapReduce編程范式 下一代MapReduce提供一個完全通用的計算框架以支持MapReduce和其他的范例. 架構允許終端用戶實現應用指定的框架,通過實現用戶的ApplicationMaster,可以向資源管理器請求資源并利用他們,因為他們通過隔離并保證資源的情況下看起來是適合的. 因此,在同一個hadoop集群下支持多種編程范式,例如MapReduce, MPI, Master-Worker和迭代模型,并允許為每個應用使用適當的框架.這對自定義框架順序執行一定數目的MapReduc應用程序(例: K-Means, Page-Rank)很重要. 結論 Apache Hadoop和特定的Hadoop MapReduce,是一個用于處理大數據集的成功開源項目. 我們建議Hadoop的 MapReduce重構以提供高可用性,增加集群利用率,提供編程范例的支持以加快發展. 我們認為,在已存在的選項中如Torque, Condor, Mesos 等,沒有一個用于設計解決MapReduce集群規模的問題, 某些功能很新且不成熟, 另外一些沒有解決關鍵問題,如調度在上十萬個task,規模的性能,安全和多用戶等. 我們將與Apache Hadoop社區合作,為實現這以提升Apache Hadoop以適應下一代大數據空間.
0.23的調度方法: http://developer.yahoo.com/blogs/hadoop/posts/2011/03/mapreduce-nextgen-scheduler/
周末無事,故翻譯sheepdog design. 原文地址: https://github.com/collie/sheepdog/wiki/Sheepdog-Design Sheepdog 設計 Sheeepdog采用完全對稱的結構,沒有類似元數據服務的中心節點. 這種設計有以下的特點. 1) 性能于容量的線性的可擴展性. 當需要提升性能或容量時,只需向集群中增加新的機器便能使Sheeepdog線性成長. 2) 沒有單點故障 即使某臺機器發生故障,依然可以通過其他機器訪問數據. 3) 容易管理 不需要配置機器角色,當管理員在新增的機器開啟Sheepdog守護進程時, Sheepdog會自動檢測新加入的機器并配置它成為存儲系統中的一員. 結構概述  Sheepdog是一個分布式存儲系統.為Sheepdog客戶端(QEMU的塊驅動程序)提供對象存儲(類似于簡單的鍵值對). 接下來幾章將更加詳細的闡述Sheepdog各個部分. 1) 對象存儲(Object對象存儲(Object Storage) 2) ) Sheepdog不同于一般的文件系統, Sheepdog進程為QEMU(Sheepdog進程名)創建一個分布式對象存儲系統,它可以存儲”對象”.這里的”對象”數據大小可變,并有唯一的標識,通過標識可以進行讀/寫/創建/刪除操作.對象存儲組成”網關”和”對象管理器”. 3) 網關(getway) Getway接收QEMU塊驅動的I/O請求(對象id,偏移,長度和操作類型),通過一直散列算法獲得目標節點,然后轉發I/O請求至該節點. 4) 對象管理器(Object manager) 對象管理器接收getway轉發過來的I/O請求,然后對磁盤執行讀/寫操作. 5) 集群管理器(Cluster manager) 集群管理器管理管理節點成員(探測失敗/新增節點并報告節點成員的變化),以及一些需要節點一致的操作(vdi 創建, 快照 vdi等).當前集群管理器使用corosync集群引擎. 6) QEMU 塊驅動 QEMU塊驅動把VM image分為固定大小(默認4M),并通過其getway存儲到對象存儲中 對象存儲(Object Storage) 每個對象使用一個64bit的整數作為全局標識,并在多臺機器存有備份,QEMU塊驅動并不關心存儲的位置,對象存儲系統負責管理存儲的位置. 對象類型(object types) Sheepdog的對象分為以下四種: 1) 數據類型(data object) 它包括虛擬磁盤映射的真實數據,虛擬磁盤映射分為固定大小的數據對象, Sheepdog客戶端訪問這個對象. 2) vdi object 它包括虛擬磁盤映射的元數據(例:映射名,磁盤大小,創建時間,vdi的數據對象ID等). 3) vmstate object 它存儲運行中的VM狀態映射.管理員通過它獲取實時快照信息. 4) vdi attr object 使用它存儲各個vdi的屬性,屬性為鍵值對類型,類似于普通文件的擴展信息. 對象ID規則(object ID rules) 1) 0 - 31 (32 bits): 對象類型詳細信息 2) 32 - 55 (24 bits): vdi id 3) 56 - 59 ( 4 bits): 預留 4) 60 - 63 ( 4 bits): 對象類型標識符 每個VDI有一個全局唯一的ID(vdi id), 通過VDI名求得的散列值,低三十二位使用如下: 對象類型 | 低32位的作用 | 數據類型 | 虛擬磁盤映射的索引號 | Vdi對象 | 未使用(填0) | Vm狀態對象 | Vm狀態映射索引 | Vdi屬性對象 | 鍵名的散列值 | 對象格式(object format) 1) 數據對象 虛擬磁盤映射的塊 2) Vdi對象 1 struct sheepdog_inode { 2 char name[SD_MAX_VDI_LEN]; /* the name of this VDI*/ 3 char tag[SD_MAX_VDI_TAG_LEN]; /* the snapshot tag name */ 4 uint64_t ctime; /* creation time of this VDI */ 5 uint64_t snap_ctime; /* the time snapshot is taken */ 6 uint64_t vm_clock_nsec; /* vm clock (used for live snapshot) */ 7 uint64_t vdi_size; /* the size of VDI */ 8 uint64_t vm_state_size; /* the size of vm state (used for live snapshot) */ 9 uint16_t copy_policy; /* reserved */ 10 uint8_t nr_copies; /* the number of object redundancy */ 11 uint8_t block_size_shift; /* info about the size of the data object */ 12 uint32_t snap_id; /* the snapshot id */ 13 uint32_t vdi_id; /* the vdi id */ 14 uint32_t parent_vdi_id; /* the parent snapshot vdi id of this VDI */ 15 uint32_t child_vdi_id[MAX_CHILDREN]; /* the children VDIs of this VDI */ 16 uint32_t data_vdi_id[MAX_DATA_OBJS]; /* the data object IDs this VDI contains*/ 17 }; 3) Vm狀態對象 Vm狀態映射塊 4) Vdi屬性對象 前SD_MAX_VDI_ATTR_KEY_LEN位(256位)為屬性的鍵名,余下的是屬性指. 只讀/可寫對象(read-only/writable objects) 從如何訪問對象的角度,我們還可以把Sheepdog對象分為以下兩類. 1) 只讀對象(例:VDI快照數據對象) 只允許一個VM對其讀寫,其他vm無法訪問 2) 可寫對象 不允許任何VM對其寫,所有VM都可讀 其他功能(other features) Sheepdog對象存儲可接收正在寫時復制(copy-on-write)的請求.當一個客戶端發送一個創建和寫的請求時,同時可以指定基本對象(CoW操作的來源),這用于快照和克隆操作. 網關(Gateway) 對象存在哪(where to store objects) Sheepdog使用一致性哈希算法決定存放對象的位置,一致性哈希算法提供哈希表,而且增加或介紹節點不回顯著的改變對象映射,通過哈希表能使I/O負載均衡. 副本(replication) Sheepdog的數據副本很簡單,我們假設只有一個寫,所以寫沖突不會發生,客戶端可以并行發生請求到目標節點,發生讀請求到一個目標節點如果客戶端自己處理I/O請求順序. 寫I/O流(write I/O flow) Getway使用一致性哈希算法計算目標節點并發送寫請求到所有目標節點,只有所有副本均更新成功寫請求才會成功,這是因為如果一個副本未更新,getway有可能從未更新的節點讀取舊數據. 讀I/O流(read I/O flow) Getway使用一致性哈希算法計算目標節點,并發送讀請求到一個目標節點. 1) 修復對象一致性 當某節點在轉發I/O請求時crash,有可能破壞副本的一致性,所以當getway第一次讀取對象時會試圖修復其一致性,從一節點讀取整個對象并用它覆蓋所有副本. 重試I/O請求(retrying I/O requests) Sheepdog存儲所有成員節點的歷史信息,我們把歷史版本號叫做”epoch”(詳見章節’對象恢復’). 如果getway轉發I/O請求至目標節點并且getway與目標節點epoch號不相符,I/O請求失敗且getway重試請求直到epcho號相符,這就需要保持副本強一致性. I/O重試也可能發生在目標節點掛了導致無法完成I/O操作. 對象管理器(Object Manager) 對象管理器把對象存儲到本地磁盤,當前把每個對象存儲為一個文件,這中方法簡單.我們也可以使用DBMS(例: BerkeleyDB, Tokyo Cabinet等) 作為對象存儲器,但還不支持. 路徑命名規則(path name rule) 對象存儲成如下路徑: /store_dir/obj/[epoch number]/[object ID] 所有對象文件有一個擴展屬性: 副本數(sheepdog.copies), 寫日志(write journaling) 當sheep進程在寫操作過程中失敗,對象有可能至少部分更新,一般情況這不會有問題,因為如果VM未接收成功消息,不保證所寫部分的內容.然而對于vdi對象,我們必須整體更新或整體未更新,因為如果vdi對象只是部分更新,VDI的元數據有可能被破壞. 為例防止這個問題,我們使用日志記錄對vdi對象的寫操作. 日志過程很簡單: 1) 創建日志文件"/store_dir/journal/[epoch]/[vdi object id]" 2) 首先寫數據到日志文件 3) 寫一個數據到vdi對象 4) 刪除日志文件 集群管理器(Cluster Manager) 大多情況, Sheepdo客戶端單獨訪問它們的映射因為我們不允許兩個客戶端同時訪問一個映射,但是某些VDI操作(例:克隆VDI,創建VDI)必須做,因為這些操作更新全局信息,我們使用Corosync集群引擎完成而不是中心服務器. 我們將擴展Sheepdog以支持其他集群管理系統. 本章正在編輯 QEMU 塊驅動(QEMU Block Driver)  Sheepdog卷被分為4M的數據對象,剛創建的對象未分配,也就是說,只有寫對象被分配. Open 首先QEMU塊驅動通過getway的bdrv_open()從對象存儲讀取vdi 讀/寫(read/write) 塊驅動通過請求的部分偏移量和大小計算數據對象id, 并向getway發送請求. 當塊驅動發送寫請求到那些不屬于其當前vdi的數據對象是,塊驅動發送CoW請求分配一個新的數據對象. 寫入快照vdi(write to snapshot vdi) 我們可以把快照VDI附加到QEMU, 當塊驅動第一次發送寫請求到快照VDI, 塊驅動創建一個新的可寫VDI作為子快照,并發送請求到新的VDI. VDI操作(VDI Operations) 查找(lookup) 當查找VDI對象時: 1) 通過求vdi名的哈希值得到vdi id 2) 通過vdi id計算di對象 3) 發送讀請求到vdi對象 4) 如果此vdi不是請求的那個,增加vdi id并重試發送讀請求 快照,克隆(snapshot, cloning) 快照可克隆操作很簡單, 1) 讀目標VDI 2) 創建一個與目標一樣的新VDI 3) 把新vdi的‘'parent_vdi_id''設為目標VDI的id 4) 設置目標vdi的''child_vdi_id''為新vdi的id. 5) 設置目標vdi的''snap_ctime''為當前時間, 新vdi變為當前vdi對象 刪除(delete) TODO:當前,回收未使用的數據對象是不會被執行,直到所有相關VDI對象(相關的快照VDI和克隆VDI)被刪除. 所有相關VDI被刪除后, Sheepdog刪除所有此VDI的數據對象,設置此VDI對象名為空字符串. 對象恢復(Object Recovery) epoch Sheepdog把成員節點歷史存儲在存儲路徑, 路徑名如下: /store_dir/epoch/[epoch number] 每個文件包括節點在epoch的列表信息(IP地址,端口,虛擬節點個數). 恢復過程(recovery process) 1) 從所有節點接收存儲對象ID 2) 計算選擇那個對象 3) 創建對象ID list文件"/store_dir/obj/[the current epoch]/list" 4) 發送一個讀請求以獲取id存在于list文件的對象. 這個請求被發送到包含前一次epoch的對象的節點.( The requests are sent to the node which had the object at the previous epoch.) 5) 把對象存到當前epoch路徑. 沖突的I/O(conflicts I/Os) 如果QEMU發送I/O請求到某些未恢復的對象, Sheepdog阻塞此請求并優先恢復對象. 協議(Protocol) Sheepdog的所有請求包含固定大小的頭部(48位)和固定大小的數據部分,頭部包括協議版本,操作碼,epoch號,數據長度等. between sheep and QEMU 操作碼 | 描述 | SD_OP_CREATE_AND_WRITE_OBJ | 發送請求以創建新對象并寫入數據,如果對象存在,操作失敗 | SD_OP_READ_OBJ | 讀取對象中的數據 | SD_OP_WRITE_OBJ | 向對象寫入數據,如果對象不存在,失敗 | SD_OP_NEW_VDI | 發送vdi名到對象存儲并創建新vdi對象, 返回應答vdi的唯一的vdi id | SD_OP_LOCK_VDI | 與SD_OP_GET_VDI_INFO相同 | SD_OP_RELEASE_VDI | 未使用 | SD_OP_GET_VDI_INFO | 獲取vdi信息(例:vdi id) | SD_OP_READ_VDIS | 獲取已經使用的vdi id | between sheep and collie 操作碼 | 描述 | SD_OP_DEL_VDI | 刪除VDI | SD_OP_GET_NODE_LIST | 獲取sheepdog的節點列表 | SD_OP_GET_VM_LIST | 未使用 | SD_OP_MAKE_FS | 創建sheepdog集群 | SD_OP_SHUTDOWN | 停止sheepdog集群 | SD_OP_STAT_SHEEP | 獲取本地磁盤使用量 | SD_OP_STAT_CLUSTER | 獲取sheepdog集群信息 | SD_OP_KILL_NODE | 退出sheep守護進程 | SD_OP_GET_VDI_ATTR | 獲取vdi屬性對象id | between sheeps 操作碼 | 描述 | SD_OP_REMOVE_OBJ | 刪除對象 | SD_OP_GET_OBJ_LIST | 獲取對象id列表,并存儲到目標節點 |
Ant是什么? Ant是一種基于 Java和XML的build工具. 1 編寫build.xml Ant的buildfile是用XML寫的.每個buildfile含有一個project. buildfile中每個task元素可以有一個id屬性,可以用這個id值引用指定的任務.這個值  是唯一的.(詳情請參考下面的Task小節) 1.1 Projects project有下面的屬性: Attribute | Description | Required | name | 項目名稱. | No | default | 當沒有指定target時使用的缺省target | Yes | basedir | 用于計算所有其他路徑的基路徑.該屬性可以被basedir property覆蓋.當覆蓋時,該屬性被忽略.如果屬性和basedir property都沒有設定,就使用buildfile文件的父目錄. | No | 項目的描述以一個頂級的<description>元素的形式出現(參看description小節).
一個項目可以定義一個或多個target.一個target是一系列你想要執行的.執行Ant時,你可以選擇執行那個target.當沒有給定target時,使用project的default屬性所確定的target.
1.2 Targets
一個target可以依賴于其他的target.例如,你可能會有一個target用于編譯程序,一個target用于生成可執行文件.你在生成可執行文件之前 先編譯通過, 生成可執行文件的target依賴于編譯target.Ant會處理這種依賴關系.
然而,應當注意到,Ant的depends屬性只指定了target應該被執行的順序-如果被依賴的target無法運行,這種depends對于指定了依賴關系的target就沒有影響.
Ant會依照depends屬性中target出現的順序(從左到右)依次執行每個target.然而,要記住的是只要某個target依賴于一個target,后者就會被先執行. <target name="A"/> <target name="B" depends="A"/> <target name="C" depends="B"/> <target name="D" depends="C,B,A"/> 假定我們要執行target D.從它的依賴屬性來看,你可能認為先執行C,然后B, A被執行.錯了,C依賴于B,B依賴于A, 先執行A,然后B,然后C, D被執行.
一個target只能被執行一次,即時有多個target依賴于它(看上面的例子).
如 果(或如果不)某些屬性被設定,才執行某個target.這樣,允許根據系統的狀態(java version, OS, 命令行屬性定義等等)來更好地控制build的過程.要想讓一個target這樣做,你就應該在target元素中,加入if(或unless)屬性,帶 上target因該有所判斷的屬性.例如: <target name="build-module-A" if="module-A-present"/> <target name="build-own-fake-module-A" unless="module-A-present"/> 如果沒有if或unless屬性,target總會被執行.
可選的description屬性可用來提供關于target的一行描述,這些描述可由-projecthelp命令行選項輸出. 將你的tstamp task在一個所謂的初始化target是很好的做法,其他的target依賴這個初始化target.要確保初始化target是出現在其他target依賴表中的第一個target.在本手冊中大多數的初始化target的名字是"init". target有下面的屬性: Attribute | Description | Required | name | target的名字 | Yes | depends | 用逗號分隔的target的名字列表,也就是依賴表. | No | if | 執行target所需要設定的屬性名. | No | unless | 執行target需要清除設定的屬性名. | No | description | 關于target功能的簡短描述. | No | 1.3 Tasks
一個task是一段可執行的代碼.
一個task可以有多個屬性(如果你愿意的話,可以將其稱之為變量).屬性只可能包含對property的引用.這些引用會在task執行前被解析.
下面是Task的一般構造形式: <name attribute1="value1" attribute2="value2" ... /> 這里name是task的名字,attributeN是屬性名,valueN是屬性值.
有一套內置的(built-in)task,以及一些可選task,但你也可以編寫自己的task.
所有的task都有一個task名字屬性.Ant用屬性值來產生日志信息.
可以給task賦一個id屬性: <taskname id="taskID" ... /> 這里taskname是task的名字,而taskID是這個task的唯一標識符.通過這個標識符,你可以在腳本中引用相應的task.例如,在腳本中你可以這樣: <script ... > task1.setFoo("bar"); </script> 設定某個task實例的foo屬性.在另一個task中(用java編寫),你可以利用下面的語句存取相應的實例. project.getReference("task1"). 注意1:如果task1還沒有運行,就不會被生效(例如:不設定屬性),如果你在隨后配置它,你所作的一切都會被覆蓋.
注意2:未來的Ant版本可能不會兼容這里所提的屬性, 很有可能根本沒有task實例,只有proxies.
1.4 Properties
一個project可以有很多的properties.可以在buildfile中用 property task來設定,或在Ant之外設定.一個property有一個名字和一個值.property可用于task的屬性值.這是通過將屬性名放在"${" 和"}"之間并放在屬性值的位置來實現的.例如如果有一個property builddir的值是"build",這個property就可用于屬性值:${builddir}/classes.這個值就可被解析為build /classes. 內置屬性 如果你使用了<property> task 定義了所有的系統屬性,Ant允許你使用這些屬性.例如,${os.name}對應操作系統的名字. 要想得到系統屬性的列表可參考the Javadoc of System.getProperties. 除了Java的系統屬性,Ant還定義了一些自己的內置屬性: basedir project基目錄的絕對路徑 (與<project>的basedir屬性一樣). ant.file buildfile的絕對路徑. ant.version Ant的版本. ant.project.name 當前執行的project的名字;由<project>的name屬性設定. ant.java.version Ant檢測到的JVM的版本; 目前的值有"1.1", "1.2", "1.3" and "1.4". 例子 <project name="MyProject" default="dist" basedir="."> <!-- set global properties for this build --> <property name="src" value="."/> <property name="build" value="build"/> <property name="dist" value="dist"/> <target name="init"> <!-- Create the time stamp --> <tstamp/> <!-- Create the build directory structure used by compile --> <mkdir dir="${build}"/> </target> <target name="compile" depends="init"> <!-- Compile the java code from ${src} into ${build} --> <javac srcdir="${src}" destdir="${build}"/> </target> <target name="dist" depends="compile"> <!-- Create the distribution directory --> <mkdir dir="${dist}/lib"/> <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file --> <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/> </target> <target name="clean"> <!-- Delete the ${build} and ${dist} directory trees --> <delete dir="${build}"/> <delete dir="${dist}"/> </target> </project> 1.5 Path-like Structures 你可以用":"和";"作為分隔符,指定類似PATH和CLASSPATH的引用.Ant會把分隔符轉換為當前系統所用的分隔符. 當需要指定類似路徑的值時,可以使用嵌套元素.一般的形式是 <classpath> <pathelement path="${classpath}"/> <pathelement location="lib/helper.jar"/> </classpath> location屬性指定了相對于project基目錄的一個文件和目錄,而path屬性接受逗號或分號分隔的一個位置列表.path屬性一般用作預定義的路徑--其他情況下,應該用多個location屬性. 為簡潔起見,classpath標簽支持自己的path和location屬性.  : <classpath> <pathelement path="${classpath}"/> </classpath> 可以被簡寫作: <classpath path="${classpath}"/> 也可通過<fileset>元素指定路徑.構成一個fileset的多個文件加入path-like structure的順序是未定的. <classpath> <pathelement path="${classpath}"/> <fileset dir="lib"> <include name="**/*.jar"/> </fileset> <pathelement location="classes"/> </classpath> 上面的例子構造了一個路徑值包括:${classpath}的路徑,跟著lib目錄下的所有jar文件,接著是classes目錄. 如果你想在多個task中使用相同的path-like structure,你可以用<path>元素定義他們(與target同級),然后通過id屬性引用--參考Referencs例子.
path-like structure可能包括對另一個path-like structurede的引用(通過嵌套<path>元素): <path id="base.path"> <pathelement path="${classpath}"/> <fileset dir="lib"> <include name="**/*.jar"/> </fileset> <pathelement location="classes"/> </path> <path id="tests.path"> <path refid="base.path"/> <pathelement location="testclasses"/> </path> 前面所提的關于<classpath>的簡潔寫法對于<path>也是有效的,如: <path id="tests.path"> <path refid="base.path"/> <pathelement location="testclasses"/> </path> 可寫成: <path id="base.path" path="${classpath}"/> 命令行變量
有些task可接受參數,并將其傳遞給另一個進程.為了能在變量中包含空格字符,可使用嵌套的arg元素. Attribute Description Required value 一個命令行變量;可包含空格字符. 只能用一個 line 空格分隔的命令行變量列表. file 作為命令行變量的文件名;會被文件的絕對名替代. path 一個作為單個命令行變量的path-like的字符串;或作為分隔符,Ant會將其轉變為特定平臺的分隔符.
例子 <arg value="-l -a"/> 是一個含有空格的單個的命令行變量. <arg line="-l -a"/> 是兩個空格分隔的命令行變量. <arg path="/dir;/dir2:dir3"/> 是一個命令行變量,其值在DOS系統上為dir;dir2;dir3;在Unix系統上為/dir:/dir2:/dir3 .
References
buildfile元素的id屬性可用來引用這些元素.如果你需要一遍遍的復制相同的XML代碼塊,這一屬性就很有用--如多次使用<classpath>結構.
下面的例子: <project ... > <target ... > <rmic ...> <classpath> <pathelement location="lib/"/> <pathelement path="${java.class.path}/"/> <pathelement path="${additional.path}"/> </classpath> </rmic> </target> <target ... > <javac ...> <classpath> <pathelement location="lib/"/> <pathelement path="${java.class.path}/"/> <pathelement path="${additional.path}"/> </classpath> </javac> </target> </project> 可以寫成如下形式: <project ... > <path id="project.class.path"> <pathelement location="lib/"/> <pathelement path="${java.class.path}/"/> <pathelement path="${additional.path}"/> </path> <target ... > <rmic ...> <classpath refid="project.class.path"/> </rmic> </target> <target ... > <javac ...> <classpath refid="project.class.path"/> </javac> </target> </project> 所有使用PatternSets, FileSets 或 path-like structures嵌套元素的task也接受這種類型的引用. 轉自: http://www.linux521.com/2009/java/200904/1760.html
|