Hadoop 新 MapReduce 框架 Yarn 詳解

tingfengjushi發表於2014-07-18

Hadoop MapReduceV2(Yarn) 框架簡介

原 Hadoop MapReduce 框架的問題

對於業界的大資料儲存及分散式處理系統來說,Hadoop 是耳熟能詳的卓越開源分散式檔案儲存及處理框架,對於 Hadoop 框架的介紹在此不再累述,讀者可參考 Hadoop 官方簡介。使用和學習過老 Hadoop 框架(0.20.0 及之前版本)的同仁應該很熟悉如下的原 MapReduce 框架圖:


圖 1.Hadoop 原 MapReduce 架構
圖 1.Hadoop 原 MapReduce 架構 

從上圖中可以清楚的看出原 MapReduce 程式的流程及設計思路:

  1. 首先使用者程式 (JobClient) 提交了一個 job,job 的資訊會傳送到 Job Tracker 中,Job Tracker 是 Map-reduce 框架的中心,他需要與叢集中的機器定時通訊 (heartbeat), 需要管理哪些程式應該跑在哪些機器上,需要管理所有 job 失敗、重啟等操作。
  2. TaskTracker 是 Map-reduce 叢集中每臺機器都有的一個部分,他做的事情主要是監視自己所在機器的資源情況。
  3. TaskTracker 同時監視當前機器的 tasks 執行狀況。TaskTracker 需要把這些資訊通過 heartbeat 傳送給 JobTracker,JobTracker 會蒐集這些資訊以給新提交的 job 分配執行在哪些機器上。上圖虛線箭頭就是表示訊息的傳送 - 接收的過程。

可以看得出原來的 map-reduce 架構是簡單明瞭的,在最初推出的幾年,也得到了眾多的成功案例,獲得業界廣泛的支援和肯定,但隨著分散式系統叢集的規模和其工作負荷的增長,原框架的問題逐漸浮出水面,主要的問題集中如下:

  1. JobTracker 是 Map-reduce 的集中處理點,存在單點故障。
  2. JobTracker 完成了太多的任務,造成了過多的資源消耗,當 map-reduce job 非常多的時候,會造成很大的記憶體開銷,潛在來說,也增加了 JobTracker fail 的風險,這也是業界普遍總結出老 Hadoop 的 Map-Reduce 只能支援 4000 節點主機的上限。
  3. 在 TaskTracker 端,以 map/reduce task 的數目作為資源的表示過於簡單,沒有考慮到 cpu/ 記憶體的佔用情況,如果兩個大記憶體消耗的 task 被排程到了一塊,很容易出現 OOM。
  4. 在 TaskTracker 端,把資源強制劃分為 map task slot 和 reduce task slot, 如果當系統中只有 map task 或者只有 reduce task 的時候,會造成資源的浪費,也就是前面提過的叢集資源利用的問題。
  5. 原始碼層面分析的時候,會發現程式碼非常的難讀,常常因為一個 class 做了太多的事情,程式碼量達 3000 多行,,造成 class 的任務不清晰,增加 bug 修復和版本維護的難度。
  6. 從操作的角度來看,現在的 Hadoop MapReduce 框架在有任何重要的或者不重要的變化 ( 例如 bug 修復,效能提升和特性化 ) 時,都會強制進行系統級別的升級更新。更糟的是,它不管使用者的喜好,強制讓分散式叢集系統的每一個使用者端同時更新。這些更新會讓使用者為了驗證他們之前的應用程式是不是適用新的 Hadoop 版本而浪費大量時間。

新 Hadoop Yarn 框架原理及運作機制

從業界使用分散式系統的變化趨勢和 hadoop 框架的長遠發展來看,MapReduce 的 JobTracker/TaskTracker 機制需要大規模的調整來修復它在可擴充套件性,記憶體消耗,執行緒模型,可靠性和效能上的缺陷。在過去的幾年中,hadoop 開發團隊做了一些 bug 的修復,但是最近這些修復的成本越來越高,這表明對原框架做出改變的難度越來越大。

為從根本上解決舊 MapReduce 框架的效能瓶頸,促進 Hadoop 框架的更長遠發展,從 0.23.0 版本開始,Hadoop 的 MapReduce 框架完全重構,發生了根本的變化。新的 Hadoop MapReduce 框架命名為 MapReduceV2 或者叫 Yarn,其架構圖如下圖所示:


圖 2. 新的 Hadoop MapReduce 框架(Yarn)架構
圖 2. 新的 Hadoop MapReduce 框架(Yarn)架構 

重構根本的思想是將 JobTracker 兩個主要的功能分離成單獨的元件,這兩個功能是資源管理和任務排程 / 監控。新的資源管理器全域性管理所有應用程式計算資源的分配,每一個應用的 ApplicationMaster 負責相應的排程和協調。一個應用程式無非是一個單獨的傳統的 MapReduce 任務或者是一個 DAG( 有向無環圖 ) 任務。ResourceManager 和每一臺機器的節點管理伺服器能夠管理使用者在那臺機器上的程式並能對計算進行組織。

事實上,每一個應用的 ApplicationMaster 是一個詳細的框架庫,它結合從 ResourceManager 獲得的資源和 NodeManager 協同工作來執行和監控任務。

上圖中 ResourceManager 支援分層級的應用佇列,這些佇列享有叢集一定比例的資源。從某種意義上講它就是一個純粹的排程器,它在執行過程中不對應用進行監控和狀態跟蹤。同樣,它也不能重啟因應用失敗或者硬體錯誤而執行失敗的任務。

ResourceManager 是基於應用程式對資源的需求進行排程的 ; 每一個應用程式需要不同型別的資源因此就需要不同的容器。資源包括:記憶體,CPU,磁碟,網路等等。可以看出,這同現 Mapreduce 固定型別的資源使用模型有顯著區別,它給叢集的使用帶來負面的影響。資源管理器提供一個排程策略的外掛,它負責將叢集資源分配給多個佇列和應用程式。排程外掛可以基於現有的能力排程和公平排程模型。

上圖中 NodeManager 是每一臺機器框架的代理,是執行應用程式的容器,監控應用程式的資源使用情況 (CPU,記憶體,硬碟,網路 ) 並且向排程器彙報。

每一個應用的 ApplicationMaster 的職責有:向排程器索要適當的資源容器,執行任務,跟蹤應用程式的狀態和監控它們的程式,處理任務的失敗原因。

新舊 Hadoop MapReduce 框架比對

讓我們來對新舊 MapReduce 框架做詳細的分析和對比,可以看到有以下幾點顯著變化:

首先客戶端不變,其呼叫 API 及介面大部分保持相容,這也是為了對開發使用者透明化,使其不必對原有程式碼做大的改變 ( 詳見 2.3 Demo 程式碼開發及詳解),但是原框架中核心的 JobTracker 和 TaskTracker 不見了,取而代之的是 ResourceManager, ApplicationMaster 與 NodeManager 三個部分。

我們來詳細解釋這三個部分,首先 ResourceManager 是一箇中心的服務,它做的事情是排程、啟動每一個 Job 所屬的 ApplicationMaster、另外監控 ApplicationMaster 的存在情況。細心的讀者會發現:Job 裡面所在的 task 的監控、重啟等等內容不見了。這就是 AppMst 存在的原因。ResourceManager 負責作業與資源的排程。接收 JobSubmitter 提交的作業,按照作業的上下文 (Context) 資訊,以及從 NodeManager 收集來的狀態資訊,啟動排程過程,分配一個 Container 作為 App Mstr

NodeManager 功能比較專一,就是負責 Container 狀態的維護,並向 RM 保持心跳。

ApplicationMaster 負責一個 Job 生命週期內的所有工作,類似老的框架中 JobTracker。但注意每一個 Job(不是每一種)都有一個 ApplicationMaster,它可以執行在 ResourceManager 以外的機器上。

Yarn 框架相對於老的 MapReduce 框架什麼優勢呢?我們可以看到:

  1. 這個設計大大減小了 JobTracker(也就是現在的 ResourceManager)的資源消耗,並且讓監測每一個 Job 子任務 (tasks) 狀態的程式分散式化了,更安全、更優美。
  2. 在新的 Yarn 中,ApplicationMaster 是一個可變更的部分,使用者可以對不同的程式設計模型寫自己的 AppMst,讓更多型別的程式設計模型能夠跑在 Hadoop 叢集中,可以參考 hadoop Yarn 官方配置模板中的 mapred-site.xml 配置。
  3. 對於資源的表示以記憶體為單位 ( 在目前版本的 Yarn 中,沒有考慮 cpu 的佔用 ),比之前以剩餘 slot 數目更合理。
  4. 老的框架中,JobTracker 一個很大的負擔就是監控 job 下的 tasks 的執行狀況,現在,這個部分就扔給 ApplicationMaster 做了,而 ResourceManager 中有一個模組叫做 ApplicationsMasters( 注意不是 ApplicationMaster),它是監測 ApplicationMaster 的執行狀況,如果出問題,會將其在其他機器上重啟。
  5. Container 是 Yarn 為了將來作資源隔離而提出的一個框架。這一點應該借鑑了 Mesos 的工作,目前是一個框架,僅僅提供 java 虛擬機器記憶體的隔離 ,hadoop 團隊的設計思路應該後續能支援更多的資源排程和控制 , 既然資源表示成記憶體量,那就沒有了之前的 map slot/reduce slot 分開造成叢集資源閒置的尷尬情況。

新的 Yarn 框架相對舊 MapRduce 框架而言,其配置檔案 , 啟停指令碼及全域性變數等也發生了一些變化,主要的改變如下:


表 1. 新舊 Hadoop 指令碼 / 變數 / 位置變化表
改變項 原框架中 新框架中(Yarn) 備註
配置檔案位置 ${hadoop_home_dir}/conf ${hadoop_home_dir}/etc/hadoop/ Yarn 框架也相容老的 ${hadoop_home_dir}/conf 位置配置,啟動時會檢測是否存在老的 conf 目錄,如果存在將載入 conf 目錄下的配置,否則載入 etc 下配置
啟停指令碼 ${hadoop_home_dir}/bin/start(stop)-all.sh ${hadoop_home_dir}/sbin/start(stop)-dfs.sh
${hadoop_home_dir}/bin/start(stop)-all.sh
新的 Yarn 框架中啟動分散式檔案系統和啟動 Yarn 分離,啟動 / 停止分散式檔案系統的命令位於 ${hadoop_home_dir}/sbin 目錄下,啟動 / 停止 Yarn 框架位於 ${hadoop_home_dir}/bin/ 目錄下
JAVA_HOME 全域性變數 ${hadoop_home_dir}/bin/start-all.sh 中 ${hadoop_home_dir}/etc/hadoop/hadoop-env.sh
${hadoop_home_dir}/etc/hadoop/Yarn-env.sh
Yarn 框架中由於啟動 hdfs 分散式檔案系統和啟動 MapReduce 框架分離,JAVA_HOME 需要在 hadoop-env.sh 和 Yarn-env.sh 中分別配置
HADOOP_LOG_DIR 全域性變數 不需要配置 ${hadoop_home_dir}/etc/hadoop/hadoop-env.sh 老框架在 LOG,conf,tmp 目錄等均預設為指令碼啟動的當前目錄下的 log,conf,tmp 子目錄 
Yarn 新框架中 Log 預設建立在 Hadoop 使用者的 home 目錄下的 log 子目錄,因此最好在 ${hadoop_home_dir}/etc/hadoop/hadoop-env.sh 配置 HADOOP_LOG_DIR,否則有可能會因為你啟動 hadoop 的使用者的 .bashrc 或者 .bash_profile 中指定了其他的 PATH 變數而造成日誌位置混亂,而該位置沒有訪問許可權的話啟動過程中會報錯

由於新的 Yarn 框架與原 Hadoop MapReduce 框架相比變化較大,核心的配置檔案中很多項在新框架中已經廢棄,而新框架中新增了很多其他配置項,看下錶所示會更加清晰:


表 2. 新舊 Hadoop 框架配置項變化表
配置檔案 配置項 Hadoop 0.20.X 配置 Hadoop 0.23.X 配置 說明
core-site.xml 系統預設分散式檔案 URI fs.default.name fs.defaultFS
hdfs-site.xml DFS name node 存放 name table 的目錄 dfs.name.dir dfs.namenode.name.dir 新框架中 name node 分成 dfs.namenode.name.dir( 存放 naname table 和 dfs.namenode.edits.dir(存放 edit 檔案),預設是同一個目錄
DFS data node 存放資料 block 的目錄 dfs.data.dir dfs.datanode.data.dir 新框架中 DataNode 增加更多細節配置,位於 dfs.datanode. 配置項下,如dfs.datanode.data.dir.perm(datanode local 目錄預設許可權);dfs.datanode.address(datanode 節點監聽埠);等
分散式檔案系統資料塊複製數 dfs.replication dfs.replication 新框架與老框架一致,值建議配置為與分散式 cluster 中實際的 DataNode 主機數一致
mapred-site.xml Job 監控地址及埠 mapred.job.tracker 新框架中已改為 Yarn-site.xml 中的 resouceManager 及 nodeManager 具體配置項,新框架中歷史 job 的查詢已從 Job tracker 剝離,歸入單獨的mapreduce.jobtracker.jobhistory 相關配置,
第三方 MapReduce 框架 mapreduce.framework.name 新框架支援第三方 MapReduce 開發框架以支援如 SmartTalk/DGSG 等非 Yarn 架構,注意通常情況下這個配置的值都設定為 Yarn,如果沒有配置這項,那麼提交的 Yarn job 只會執行在 locale 模式,而不是分散式模式。
Yarn-site.xml The address of the applications manager interface in the RM Yarn.resourcemanager.address 新框架中 NodeManager 與 RM 通訊的介面地址
The address of the scheduler interface Yarn.resourcemanager.scheduler.address 同上,NodeManger 需要知道 RM 主機的 scheduler 排程服務介面地址
The address of the RM web application Yarn.resourcemanager.webapp.address 新框架中各個 task 的資源排程及執行狀況通過通過該 web 介面訪問
The address of the resource tracker interface Yarn.resourcemanager.resource-tracker.address 新框架中 NodeManager 需要向 RM 報告任務執行狀態供 Resouce 跟蹤,因此 NodeManager 節點主機需要知道 RM 主機的 tracker 介面地址

Hadoop Yarn 框架 Demo 示例

Demo 場景介紹:Weblogic 應用伺服器日誌分析

瞭解了 hadoop 新的 Yarn 框架的架構和思路後,我們用一個 Demo 示例來檢驗新 Yarn 框架下 Map-Reduce 程式的開發部署。

我們考慮如下應用場景:使用者的生產系統由多臺 Weblogic 應用伺服器組成,每天需要每臺對應用伺服器的日誌內容進行檢查,統計其日誌級別和日誌模組的總數。

WebLogic 的日誌範例如下圖所示:


圖 3.Weblogic 日誌示例
圖 3.Weblogic 日誌示例 

如上圖所示, 為 weblogic 的日誌級別, 為 Weblogic 的日誌模組,我們主要分析 loglevel 和 logmodule 這兩個維度分別在 WebLogic 日誌中出現的次數,每天需要統計出 loglevel 和 logmodule 分別出現的次數總數。

Demo 測試環境 Yarn 框架搭建

由於 Weblogic 應用伺服器分佈於不同的主機,且日誌資料量巨大,我們採用 hadoop 框架將 WebLogic 各個應用伺服器主機上建立分散式目錄,每天將 WebLogic 日誌裝載進 hadoop 分散式檔案系統,並且編寫基於 Yarn 框架的 MapReduce 程式對日誌進行處理,分別統計出 LogLevel 和 Logmodule 在日誌中出現的次數並計算總量,然後輸出到分散式檔案系統中,輸出目錄命名精確到小時為字尾以便區分每次 Demo 程式執行的處理結果。

我們搭建一個 Demo 測試環境以驗證 Yarn 框架下分散式程式處理該案例的功能,以兩臺虛擬機器作為該 Demo 的執行平臺,兩機均為 Linux 作業系統,機器 hostname 為 OEL 和 Stephen,OEL 作為 NameNode 和 ResouceManager 節點主機,64 位,Stephen 作為 DataNode 和 NodeManager 節點主機,32 位(Hadoop 支援異構性), 具體如下:


表 3.Demo 測試環境表
主機名 角色 備註
OEL(192.168.137.8) NameNode 節點主機 
ResourceManager 主機
linux 作業系統 
32bit
Stephen(192.168.l37.2) DataNode 節點主機 
NodeManager 主機
linux 作業系統 
64bit

我們把 hadoop 安裝在兩臺測試機的 /hadoop 檔案系統目錄下,安裝後的 hadoop 根目錄為:/hadoop/hadoop-0.23.0,規劃分散式檔案系統存放於 /hadoop/dfs 的本地目錄,對應分散式系統中的目錄為 /user/oracle/dfs

我們根據 Yarn 框架要求,分別在 core-site.xml 中配置分散式檔案系統的 URL,詳細如下:


清單 1.core-site.xml 配置
				
  
    
   fs.defaultFS 
   hdfs://192.168.137.8:9100 
    
  

在 hdfs-site.xml 中配置 nameNode,dataNode 的本地目錄資訊,詳細如下:


清單 2.hdfs-site.xml 配置
				
  
  
  dfs.namenode.name.dir 
  /hadoop/dfs/name 
     
  

  
  dfs.datanode.data.dir 
  /hadoop/dfs/data 
    
  

  
   dfs.replication 
   2 
  

  

在 mapred-site.xml 中配置其使用 Yarn 框架執行 map-reduce 處理程式,詳細如下:


清單 3.mapred-site.xml 配置
				
  
   
  mapreduce.framework.name 
  Yarn 
   
  

最後在 Yarn-site.xml 中配置 ResourceManager,NodeManager 的通訊埠,web 監控埠等,詳細如下:


清單 4.Yarn-site.xml 配置
				
  
  

 <!-- Site specific YARN configuration properties --&gt 
   
  Yarn.nodemanager.aux-services 
  mapreduce.shuffle 
   
   
  The address of the applications manager interface in the RM. 
  Yarn.resourcemanager.address 
  192.168.137.8:18040 
   

   
  The address of the scheduler interface. 
  Yarn.resourcemanager.scheduler.address 
  192.168.137.8:18030 
   

   
  The address of the RM web application. 
  Yarn.resourcemanager.webapp.address 
  192.168.137.8:18088 
   
  
   
  The address of the resource tracker interface. 
  Yarn.resourcemanager.resource-tracker.address 
  192.168.137.8:8025 
   
  

具體配置項的含義,在 hadoop 官方網站有詳細的說明,讀者可以參見 hadoop 0.23.0 官方配置模板

Demo 程式碼開發及詳解

以下我們詳細介紹一下新的 Yarn 框架下針對該應用場景的 Demo 程式碼的開發, 在 Demo 程式的每個類都有詳細的註釋和說明,Yarn 開發為了相容老版本,API 變化不大,可以參考 官方 Hadoop Yarn 框架 API

在 Map 程式中,我們以行號為 key,行文字為 value 讀取每一行 WebLogic 日誌輸入,將 loglevel 和 logmodule 的值讀出作為 Map 處理後的新的 key 值,由於一行中 loglevel 和 logmodule 的出現次數應該唯一,所以經 Map 程式處理後的新的 record 記錄的 value 應該都為 1:


清單 5. Map 業務邏輯
				
 public static class MapClass extends Mapper 
  { 
  private Text record = new Text(); 
  private static final IntWritable recbytes = new IntWritable(1); 
  public void map(Object key, Text value,Context context) 
    throws IOException,InterruptedException {  
  String line = value.toString(); 
 // 沒有配置 RecordReader,所以預設採用 line 的實現,
 //key 就是行號,value 就是行內容,
 // 按行 key-value 存放每行 loglevel 和 logmodule 內容
  if (line == null || line.equals("")) 
    return; 
  String[] words = line.split("> 
			

由於有 loglevel 和 logmodule 兩部分的分析工作,我們設定兩個 Reduce 來分別處理這兩部分,loglevel 的交給 reduce1,logmodule 交給 reduce2。因此我們編寫 Patitioner 類,根據 Map 傳過來的 Key 中包含的 logLevel 和 moduleName 的字首,來分配到不同的 Reduce:


清單 6.Partition 業務邏輯
public static class PartitionerClass extends Partitioner
{
public int getPartition(Text key, IntWritable value, int numPartitions)
{
if (numPartitions >= 2)//Reduce 個數,判斷 loglevel 還是 logmodule 的統計,分配到不同的 Reduce
if (key.toString().startsWith("logLevel::"))
return 0;
else if(key.toString().startsWith("moduleName::"))
return 1;
else return 0;
else
return 0;
}

}

在 Reduce 程式中,累加併合並 loglevel 和 logmodule 的出現次數


清單 7. Reduce 業務邏輯
				
 public static class ReduceClass extends  Reducer 
        { 
            private IntWritable result = new IntWritable(); 
            public void reduce(Text key, Iterable values, 
                    Context context)throws IOException, 
                                                     InterruptedException { 
                
                int tmp = 0; 
                for (IntWritable val : values) { 
                    tmp = tmp + val.get();                                     
                } 
                result.set(tmp); 
                context.write(key, result);// 輸出最後的彙總結果
            }    
        } 

以上完成了 MapReduce 的主要處理邏輯,對於程式入口,我們使用 Hadoop 提供的 Tools 工具包方便的進行 May-Reduce 程式的啟動和 Map/Reduce 對應處理 class 的配置。


清單 8. Main 執行類
				
 import java.io.File; 
 import java.io.IOException; 
 import java.text.SimpleDateFormat; 
 import java.util.Date; 
 import java.util.Iterator; 
 import org.apache.hadoop.conf.Configuration; 
 import org.apache.hadoop.conf.Configured; 
 import org.apache.hadoop.fs.Path; 
 import org.apache.hadoop.io.IntWritable; 
 import org.apache.hadoop.io.Text; 
 import org.apache.hadoop.mapreduce.Job; 
 import org.apache.hadoop.mapreduce.Reducer; 
 import org.apache.hadoop.mapreduce.Mapper; 
 import org.apache.hadoop.mapreduce.Partitioner; 
 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; 
 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; 
 import org.apache.hadoop.util.Tool; 
 import org.apache.hadoop.util.ToolRunner; 
 public class LogAnalysiser extends Configured implements Tool { 
   public static void main(String[] args) 
  { 
    try 
  { 
  int res; 
  res = ToolRunner.run(new Configuration(),new LogAnalysiser(), args); 
  System.exit(res); 
  } catch (Exception e) 
  { 
  e.printStackTrace(); 
  } 
  } 
  public int run(String[] args) throws Exception 
  { 
  if (args == null || args.length <2) 
  { 
  System.out.println("need inputpath and outputpath"); 
  return 1; 
  } 
  String inputpath = args[0]; 
  String outputpath = args[1]; 
  String shortin = args[0]; 
  String shortout = args[1]; 
  if (shortin.indexOf(File.separator) >= 0) 
  shortin = shortin.substring(shortin.lastIndexOf(File.separator)); 
  if (shortout.indexOf(File.separator) >= 0) 
  shortout = shortout.substring(shortout.lastIndexOf(File.separator)); 
  SimpleDateFormat formater = new SimpleDateFormat("yyyy.MM.dd.HH.mm"); 
  shortout = new StringBuffer(shortout).append("-") 
  .append(formater.format(new Date())).toString(); 
  
  
  if (!shortin.startsWith("/")) 
  shortin = "/" + shortin; 
  if (!shortout.startsWith("/")) 
  shortout = "/" + shortout; 
  shortin = "/user/oracle/dfs/" + shortin; 
  shortout = "/user/oracle/dfs/" + shortout;   
  File inputdir = new File(inputpath); 
  File outputdir = new File(outputpath); 
  
  if (!inputdir.exists() || !inputdir.isDirectory()) 
  { 
  System.out.println("inputpath not exist or isn't dir!"); 
  return 0; 
  } 
  if (!outputdir.exists()) 
  { 
  new File(outputpath).mkdirs(); 
  } 
 // 以下注釋的是 hadoop 0.20.X 老版本的 Job 程式碼,在 hadoop0.23.X 新框架中已經大大簡化
 //   Configuration conf = getConf(); 
 //   JobConf job = new JobConf(conf, LogAnalysiser.class);   
 //    JobConf conf = new JobConf(getConf(),LogAnalysiser.class);// 構建 Config 
 //    conf.setJarByClass(MapClass.class); 
 //    conf.setJarByClass(ReduceClass.class); 
 //    conf.setJarByClass(PartitionerClass.class); 
 //    conf.setJar("hadoopTest.jar"); 
 //    job.setJar("hadoopTest.jar"); 

 // 以下是新的 hadoop 0.23.X Yarn 的 Job 程式碼

 job job = new Job(new Configuration()); 
    job.setJarByClass(LogAnalysiser.class); 
    job.setJobName("analysisjob"); 
    job.setOutputKeyClass(Text.class);// 輸出的 key 型別,在 OutputFormat 會檢查
    job.setOutputValueClass(IntWritable.class); // 輸出的 value 型別,在 OutputFormat 會檢查
    job.setJarByClass(LogAnalysiser.class); 
    job.setMapperClass(MapClass.class); 
    job.setCombinerClass(ReduceClass.class); 
    job.setReducerClass(ReduceClass.class); 
    job.setPartitionerClass(PartitionerClass.class); 
    job.setNumReduceTasks(2);// 強制需要有兩個 Reduce 來分別處理流量和次數的統計
    FileInputFormat.setInputPaths(job, new Path(shortin));//hdfs 中的輸入路徑
    FileOutputFormat.setOutputPath(job,new Path(shortout));//hdfs 中輸出路徑
    
    Date startTime = new Date(); 
    System.out.println("Job started: " + startTime); 
    job.waitForCompletion(true);    
    Date end_time = new Date(); 
    System.out.println("Job ended: " + end_time); 
    System.out.println("The job took " + 
    (end_time.getTime() - startTime.getTime()) /1000 + " seconds."); 
    // 刪除輸入和輸出的臨時檔案
 //    fileSys.copyToLocalFile(new Path(shortout),new Path(outputpath)); 
 //    fileSys.delete(new Path(shortin),true); 
 //    fileSys.delete(new Path(shortout),true); 
    return 0; 
  } 
 } 

Demo 部署及執行

Demo 輸入輸出的控制

本 demo 中我們將從 Weblogic 日誌目錄中拷貝原始待處理日誌檔案作為 Yarn 程式的輸入,使用 hadoop dfs 命令將其放入分散式目錄的 input 目錄,處理完後將生成以時間戳為檔案目錄字尾的輸出目錄

Weblogic 日誌存放的原始目錄位於:/u01/app/Oracle/Middleware/user_projects/domains/test_domain/AdminServer/logs

分散式檔案系統中的輸入目錄:/user/oracle/dfs/input

分散式檔案系統中的輸出目錄:/user/oracle/dfs/output_%YYYY-MM-DD-hh-mm%

Demo 打包和部署

可以使用 JDeveloper 或者 Eclipse 等 IDE 工具將開發的 Hadoop Demo 程式碼打包為 jar,並指定 Main 類為 LoyAnalyze,本文中我們採用 JDeveloper 打包 Demo 程式碼,如下圖示例:


圖 4.Yarn Demo 程式打包示例
圖 4.Yarn Demo 程式打包示例 

Demo 執行與跟蹤

我們在 OEL 主機(NameNode&ResourceManager 主機,192.168.137.8)上啟動 dfs 分散式檔案系統:


圖 5. 啟動 Demo dfs 檔案系統
圖 5. 啟動 Demo dfs 檔案系統 

從上圖可以看出 dfs 分散式檔案系統已經在 OEL 和 Stephen 主機上成功啟動,我們通過預設的分散式檔案系統 Web 監控 http://192.168.137.8:50070(也可以在上文中 core-site.xml 中配置 dfs.namenode.http-address 項指定其他埠 ) 來驗證其檔案系統情況:


圖 6.hadoop 檔案系統 web 監控頁面
圖 6.hadoop 檔案系統 web 監控頁面 

從上圖中我們可以看到 /user/oracle/dfs 分散式檔案系統已成功建立。

接下來我們在 NameNode 主機(OEL,192.168.137.8)上啟動 Yarn 框架:


圖 7. 啟動 Demo Yarn 框架
圖 7. 啟動 Demo Yarn 框架 

從上圖我們可以看到 ResouceManager 在 OEL 主機上成功啟動,NodeManager 程式在 Stephen 節點主機上也已經啟動,至此整個新的 Hadoop Yarn 框架已經成功啟動。

我們將打好的 testHadoop.jar 包上傳至 NameNode 主機(OEL)的 /hadoop/hadoop-0.23.0/ 根目錄下,我們使用 Hadoop 自帶的 hadoop 命令列工具執行 Demo 的 jar 包,具體步驟為,先使用 hadoop dfs 命令將輸入檔案(weblogic 原始日誌)拷貝至 dfs 分散式目錄的 input 輸入目錄,清理 dfs 分散式目錄下的 output 輸出子目錄。然後使用 hadoop jar 命令執行 testHadoop 的 jar 包。

執行 Demo 的 shell 指令碼示例如下:

 ./bin/hadoop dfs -rmr /user/oracle/dfs/output* 
 ./bin/hadoop dfs -rmr /user/oracle/dfs/input 
 ./bin/hadoop dfs -mkdir /user/oracle/dfs/input 
 ./bin/hadoop dfs -copyFromLocal ./input/*.log /user/oracle/dfs/input/ 
 ./bin/hadoop jar ./hadoopTest.jar /hadoop/hadoop-0.23.0/input 
                 /hadoop/hadoop-0.23.0/output

清單 9.Demo 執行指令碼

然後我們使用上文中的指令碼啟動 demo 並執行:


圖 8.Demo 程式執行
圖 8.Demo 程式執行 

檢視大圖

從上圖的 console 輸出中我們可以看到 Demo 程式的結果和各項統計資訊輸出,下面我們通過 Web 監控介面詳細中觀察程式執行的執行流程和步驟細節。

Job 啟動後我們可以通過 ResourceManager 的 Web 埠(在上文中 Yarn-site.xml 配置檔案中 Yarn.resourcemanager.webapp.address 配置項) http://192.168.137.8:18088 來監控其 job 的資源排程。


圖 9. 接收請求和生成 job application
圖 9. 接收請求和生成 job application 

檢視大圖

上圖中我們可以看到 Yarn 框架接受到客戶端請求 , 如上圖所示 ID 為 application_1346564668712_0003 的 job 已經是 accepted 狀態

我們點選該 ID 的連結進入到該 application 的 Map-Reduce 處理監控頁面,該介面中有動態分配的 ApplicationMaster 的 Web 跟蹤埠可以監視 MapReduce 程式的步驟細節


圖 10.hadoop MapReduce Application Web 監控頁面 (1)
圖 10.hadoop MapReduce Application Web 監控頁面 (1) 

點選上圖中 ApplicationMaster 的 URL 可以進入該 ApplicationMaster 負責管理的 Job 的具體 Map-Reduce 執行狀態:


圖 11.hadoop MasterApplication Web 監控頁面(2)
圖 11.hadoop MasterApplication Web 監控頁面(2) 

上圖中我們可以看到 ID 為 application_1346564668712_0003 的 Job 正在執行,有 2 個 Map 程式,已經處理完畢,有 2 個 Reduce 正在處理,這跟我們程式設計預期的是一樣的。

當狀態變為 successful 後,進入 dfs 檔案系統可以看到,輸出的 dfs 檔案系統已經生成,位置位於 /user/oracle/dfs 下,目錄名為 output-2012.09.02.13.52,可以看到格式和命名方式與 Demo 設計是一致的,如下圖所示:


圖 12.Demo 輸出目錄(1)
圖 12.Demo 輸出目錄(1) 

我們進入具體的輸出目錄,可以清楚的看到程式處理的輸出結果,正如我們 Demo 中設計的,兩個 Reduce 分別生成了兩個輸出檔案,分別是 part-r-00000 和 part-r-00001,對應 Module 和 Log Level 的處理輸出資訊:


圖 13.Demo 輸出目錄(2)
圖 13.Demo 輸出目錄(2) 

點選 part-r-00000 的輸出檔案連結,可以看到程式處理後的 log level 的統計資訊:


圖 14.Demo 輸出結果(1)
圖 14.Demo 輸出結果(1) 

點選 part-r-00001 的輸出檔案連結,可以看到程式處理後 Module 的統計資訊:


圖 15.Demo 輸出結果(2)
圖 15.Demo 輸出結果(2) 

至此我們基於新的 Yarn 框架的 Demo 完全成功執行,實現功能與預期設計完全一致,執行狀態和 NameNode/DataNode 部署,Job/MapReduece 程式的排程均和設計一致。讀者可參考該 Demo 的配置及程式碼進行修改,做為實際生產環境部署和實施的基礎。


下載

描述 名字 大小 下載方法
樣例程式碼 source_code.zip 4.7KB HTTP
樣例程式碼 config_files.zip 2.1KB HTTP

關於下載方法的資訊


參考資料

學習

討論

關於作者

唐清原,現供職於一家知名 500 強 IT 企業從事高階諮詢顧問工作,對 Java,Unix/Linux,資料庫及應用伺服器方面有超過 8 年豐富經驗,曾在 developerWorks 發表過相關文章(如 《在 Hibernate 中直接操作 JDBC 介面》

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26400547/viewspace-1222264/,如需轉載,請註明出處,否則將追究法律責任。

相關文章