(轉)基於MapReduce的HBase開發

在僞分佈式模式和全分佈式模式下 HBase 是架構在 HDFS 上的,因此完全可以將MapReduce 編程框架和 HBase 結合起來使用。也就是說,將 HBase 作爲底層“存儲結構”,MapReduce 調用 HBase 進行特殊的處理,這樣能夠充分結合 HBase 分佈式大型數據庫和MapReduce 並行計算的優點。 

相對應MapReduce的hbase實現類:

  1)InputFormat 類:HBase 實現了 TableInputFormatBase 類,該類提供了對錶數據的大部分操作,其子類 TableInputFormat 則提供了完整的實現,用於處理表數據並生成鍵值對。TableInputFormat 類將數據表按照 Region 分割成 split,既有多少個 Regions 就有多個splits。然後將 Region 按行鍵分成<key,value>對,key 值對應與行健,value 值爲該行所包含的數據。 
  2)Mapper 類和 Reducer 類:HBase 實現了 TableMapper 類和 TableReducer 類,其中TableMapper 類並沒有具體的功能,只是將輸入的<key,value>對的類型分別限定爲 Result 和ImmutableBytesWritable。IdentityTableMapper 類和 IdentityTableReducer 類則是上述兩個類的具體實現,其和 Mapper 類和 Reducer 類一樣,只是簡單地將<key,value>對輸出到下一個階段。 

3)OutputFormat 類:HBase 實現的 TableOutputFormat 將輸出的<key,value>對寫到指定的 HBase 表中,該類不會對 WAL(Write-Ahead  Log)進行操作,即如果服務器發生
故障將面臨丟失數據的風險。可以使用 MultipleTableOutputFormat 類解決這個問題,該類可以對是否寫入 WAL 進行設置。

代碼:

[java] view plaincopy
  1. import java.io.IOException;   
  2. import java.util.Iterator;   
  3. import java.util.StringTokenizer;   
  4.    
  5. import org.apache.hadoop.conf.Configuration;   
  6. import org.apache.hadoop.fs.Path;   
  7. import org.apache.hadoop.hbase.HBaseConfiguration;   
  8. import org.apache.hadoop.hbase.HColumnDescriptor;   
  9. import org.apache.hadoop.hbase.HTableDescriptor;   
  10. import org.apache.hadoop.hbase.client.HBaseAdmin;   
  11. import org.apache.hadoop.hbase.client.Put;   
  12. import org.apache.hadoop.hbase.mapreduce.TableOutputFormat;   
  13. import org.apache.hadoop.hbase.mapreduce.TableReducer;   
  14. import org.apache.hadoop.hbase.util.Bytes;   
  15. import org.apache.hadoop.io.IntWritable;   
  16. import org.apache.hadoop.io.LongWritable;   
  17. import org.apache.hadoop.io.Text;   
  18. import org.apache.hadoop.io.NullWritable;   
  19. import org.apache.hadoop.mapreduce.Job;   
  20. import org.apache.hadoop.mapreduce.Mapper;   
  21. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;   
  22. import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;   
  23.    
  24. public class WordCountHBase {   
  25.    
  26.   // 實現 Map 類,只是繼承自Mapper,而不是TableMapper,所以該map是從HDFS中讀取數據的
  27.   public static class Map extends   
  28.       Mapper<LongWritable, Text, Text, IntWritable> {   
  29.     private final static IntWritable one = new IntWritable(1);   
  30.     private Text word = new Text();   
  31.    
  32.     public void map(LongWritable key, Text value, Context context)   
  33.         throws IOException, InterruptedException {   
  34.       StringTokenizer itr = new StringTokenizer(value.toString());   
  35.       while (itr.hasMoreTokens()) {   
  36.         word.set(itr.nextToken());   
  37.         context.write(word, one);   
  38.       }   
  39.     }   
  40.   }   
  41.    
  42.   // 實現 Reduce 類,繼承自TableReducer,所以該reducer將數據寫入到hbase中
  43.   public static class Reduce extends   
  44.       TableReducer<Text, IntWritable, NullWritable> {   
  45.    
  46.     public void reduce(Text key, Iterable<IntWritable> values,   
  47.         Context context) throws IOException, InterruptedException {   
  48.    
  49.       int sum = 0;   
  50.    
  51.       Iterator<IntWritable> iterator = values.iterator();   
  52.       while (iterator.hasNext()) {   
  53.         sum += iterator.next().get();   
  54.       }   
  55.    
  56.       // Put 實例化,每個詞存一行   
  57.       Put put = new Put(Bytes.toBytes(key.toString()));   
  58.       // column family爲 content,column爲 count,value爲數目   
  59.       put.add(Bytes.toBytes("content"), Bytes.toBytes("count"),   
  60.           Bytes.toBytes(String.valueOf(sum)));   
  61.    
  62.       context.write(NullWritable.get(), put);   
  63.     }   
  64.   }   
  65.    
  66.   // 創建 HBase 數據表   
  67.   public static void createHBaseTable(String tableName)    
  68. throws IOException {   
  69.     // 創建表描述   
  70.     HTableDescriptor htd = new HTableDescriptor(tableName);   
  71.     // 創建列族描述   
  72.     HColumnDescriptor col = new HColumnDescriptor("content");   
  73.     htd.addFamily(col);   
  74.    
  75.     // 配置 HBase   
  76.     Configuration conf = HBaseConfiguration.create();   
  77.    
  78.     conf.set("hbase.zookeeper.quorum","master");   
  79.     conf.set("hbase.zookeeper.property.clientPort""2181");   
  80.     HBaseAdmin hAdmin = new HBaseAdmin(conf);   
  81.    
  82.     if (hAdmin.tableExists(tableName)) {   
  83.       System.out.println("該數據表已經存在,正在重新創建。");   
  84.       hAdmin.disableTable(tableName);   
  85.       hAdmin.deleteTable(tableName);   
  86.     }   
  87.    
  88.     System.out.println("創建表:" + tableName);   
  89.     hAdmin.createTable(htd);   
  90.   }   
  91.    
  92.   public static void main(String[] args) throws Exception {   
  93.     String tableName = "wordcount";   
  94.     // 第一步:創建數據庫表   
  95.     WordCountHBase.createHBaseTable(tableName);   
  96.    
  97.     // 第二步:進行 MapReduce 處理   
  98.     // 配置 MapReduce   
  99.     Configuration conf = new Configuration();   
  100.     // 這幾句話很關鍵   
  101.     conf.set("mapred.job.tracker""master:9001");   
  102.     conf.set("hbase.zookeeper.quorum","master");   
  103.     conf.set("hbase.zookeeper.property.clientPort""2181");   
  104.     conf.set(TableOutputFormat.OUTPUT_TABLE, tableName);   
  105.    
  106.     Job job = new Job(conf, "New Word Count");   
  107.     job.setJarByClass(WordCountHBase.class);   
  108.    
  109.     // 設置 Map 和 Reduce 處理類   
  110.     job.setMapperClass(Map.class);   
  111.     job.setReducerClass(Reduce.class);   
  112.    
  113.     // 設置輸出類型   
  114.     job.setMapOutputKeyClass(Text.class);   
  115.     job.setMapOutputValueClass(IntWritable.class);   
  116.    
  117.     // 設置輸入和輸出格式   
  118.     job.setInputFormatClass(TextInputFormat.class);   
  119.     job.setOutputFormatClass(TableOutputFormat.class);   
  120.    
  121.     // 設置輸入目錄   
  122.     FileInputFormat.addInputPath(job, new Path("hdfs://master:9000/in/"));   
  123.     System.exit(job.waitForCompletion(true) ? 0 : 1);   
  124.    
  125.   }   
  126. }   


常見錯誤及解決方法:

1、java.lang.RuntimeException: java.lang.ClassNotFoundException: org.apache.hadoop.hbase.mapreduce.TableOutputFormat

錯誤輸出節選:

[java] view plaincopy
  1. 13/09/10 21:14:01 INFO mapred.JobClient: Running job: job_201308101437_0016  
  2. 13/09/10 21:14:02 INFO mapred.JobClient:  map 0% reduce 0%  
  3. 13/09/10 21:14:16 INFO mapred.JobClient: Task Id : attempt_201308101437_0016_m_000007_0, Status : FAILED  
  4. java.lang.RuntimeException: java.lang.ClassNotFoundException: org.apache.hadoop.hbase.mapreduce.TableOutputFormat  
  5.     at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:849)  
  6.     at org.apache.hadoop.mapreduce.JobContext.getOutputFormatClass(JobContext.java:235)  
  7.     at org.apache.hadoop.mapred.Task.initialize(Task.java:513)  
  8.     at org.apache.hadoop.mapred.MapTask.run(MapTask.java:353)  
  9.     at org.apache.hadoop.mapred.Child$4.run(Child.java:255)  
  10.     at java.security.AccessController.doPrivileged(Native Method)  
  11.     at javax.security.auth.Subject.doAs(Subject.java:396)  
  12.     at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1149)  
  13.     at org.apache.hadoop.mapred.Child.main(Child.java:249)  
  14. Caused by: java.lang.ClassNotFoundException: org.apache.hadoop.hbase.mapreduce.TableOutputFormat  
  15.     at java.net.URLClassLoader$1.run(URLClassLoader.java:202)  
  16.     at java.security.AccessController.doPrivileged(Native Method)  
  17.     at java.net.URLClassLoader.findClass(URLClassLoader.java:190)  
  18.     at java.lang.ClassLoader.loadClass(ClassLoader.java:306)  
  19.     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)  
  20.     at java.lang.ClassLoader.loadClass(ClassLoader.java:247)  
  21.     at java.lang.Class.forName0(Native Method)  
  22.     at java.lang.Class.forName(Class.java:249)  
  23.     at org.apache.hadoop.conf.Configuration.getClassByName(Configuration.java:802)  
  24.     at org.apache.hadoop.conf.Configuration.getClass(Configuration.java:847)  
  25.     ... 8 more  

錯誤原因:

相關的類文件沒有引入到 Hadoop 集羣上。

解決步驟:

步驟一、停止HBase數據庫:

[java] view plaincopy
  1. [hadoop@master bin]$ stop-hbase.sh   
  2. stopping hbase............  
  3. master: stopping zookeeper.  
  4. [hadoop@master bin]$ jps  
  5. 16186 Jps  
  6. 26186 DataNode  
  7. 26443 TaskTracker  
  8. 26331 JobTracker  
  9. 26063 NameNode  
   停止Hadoop集羣:

[java] view plaincopy
  1. [hadoop@master bin]$ stop-all.sh   
  2. Warning: $HADOOP_HOME is deprecated.  
  3.   
  4. stopping jobtracker  
  5. master: Warning: $HADOOP_HOME is deprecated.  
  6. master:   
  7. master: stopping tasktracker  
  8. node1: Warning: $HADOOP_HOME is deprecated.  
  9. node1:   
  10. node1: stopping tasktracker  
  11. stopping namenode  
  12. master: Warning: $HADOOP_HOME is deprecated.  
  13. master:   
  14. master: stopping datanode  
  15. node1: Warning: $HADOOP_HOME is deprecated.  
  16. node1: stopping datanode  
  17. node1:   
  18. node1: Warning: $HADOOP_HOME is deprecated.  
  19. node1:   
  20. node1: stopping secondarynamenode  
  21. [hadoop@master bin]$ jps  
  22. 16531 Jps  

步驟二、需要配置 Hadoop 集羣中每臺機器,在 hadoop 目錄的 conf 子目錄中,找 hadoop-env.sh文件,並添加如下內容:

[java] view plaincopy
  1. # set hbase environment  
  2. export HBASE_HOME=/opt/modules/hadoop/hbase/hbase-0.94.11-security  
  3. export HADOOP_CLASSPATH=$HBASE_HOME/hbase-0.94.11-security.jar:$HBASE_HOME/hbase-0.94.11-security-tests.jar:$HBASE_HOME/conf:$HBASE_HOME/lib/zookeeper-3.4.5.jar  
步驟三、重新啓動集羣和hbase數據庫。

2、Error: java.lang.ClassNotFoundException: com.google.protobuf.Message

錯誤輸出節選:

[java] view plaincopy
  1. 2013-09-12 12:38:57,833 INFO  mapred.JobClient (JobClient.java:monitorAndPrintJob(1363)) -  map 0% reduce 0%  
  2. 2013-09-12 12:39:12,490 INFO  mapred.JobClient (JobClient.java:monitorAndPrintJob(1392)) - Task Id : attempt_201309121232_0001_m_000007_0, Status : FAILED  
  3. Error: java.lang.ClassNotFoundException: com.google.protobuf.Message  
  4.     at java.net.URLClassLoader$1.run(URLClassLoader.java:202)  
  5.     at java.security.AccessController.doPrivileged(Native Method)  
  6.     at java.net.URLClassLoader.findClass(URLClassLoader.java:190)  
  7.     at java.lang.ClassLoader.loadClass(ClassLoader.java:306)  
  8.     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)  
錯誤原因:

明顯,沒找到protobuf-java-2.4.0a.jar包,將該包路徑加入hadoop-env.sh中。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章