【mapreduce】hadoop2.x—mapreduce实战和总结

来源:互联网 发布:进销存数据库设计 编辑:程序博客网 时间:2024/05/01 08:39

转自:http://blog.csdn.net/u012749168/article/details/52809278


在eclipse上编写程序,运行在hadoop上。网上很多的例子都是1.x的mr代码,而1.x的代码和2.x的代码是有些区别的。在hadoop官网上可以下载到hadoop的源码包,源码包里面有很多的源代码,hadoop安装包里面的都是些jar文件,jar可以执行,但是无法看里面的源码。接下来详细分析两个案例的代码,并附带个人的hadoop学习总结与大家分享,希望大家给点意见。一个是hadoop源码,一个是hadoop实战的例子。希望大家给点意见.....

第一:hadoop-2.7.3的wordcount的源码文件

[java] view plain copy
 print?
  1. import java.io.IOException;  
  2. import java.util.StringTokenizer;  
  3.   
  4.   
  5. import org.apache.hadoop.conf.Configuration;  
  6. import org.apache.hadoop.fs.Path;  
  7. import org.apache.hadoop.io.IntWritable;  
  8. import org.apache.hadoop.io.Text;  
  9. import org.apache.hadoop.mapreduce.Job;  
  10. import org.apache.hadoop.mapreduce.Mapper;  
  11. import org.apache.hadoop.mapreduce.Reducer;  
  12. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  13. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  14. import org.apache.hadoop.util.GenericOptionsParser;  
  15.   
  16.   
  17. public class WordCount {  
  18.   /*WordCount里面定义了两个内部类。一个是TokenizerMapper,继承Mapper类*/  
  19.   public static class TokenizerMapper   
  20.       /*Mapper类给出了4个参数,前两个代表的是map要处理的key和value的类型,默认可以指定Text,Object的范围更加宽泛,也是可以的,这里的key和value,默认采用的是以行为单位,可以理解为行号是key,实际是偏移量,每行的内容是value,词频统计非常有效,后面会说一些特殊情况。*/  
  21.        /*后两个是要输出的key和value的类型。在实际情况中,我们输出的key是以空格分割的单词,value是1(这个1后面要累加,所以用int,intwritable是hadoop的变量,hadoop要在网络中进行计算传递,所用的数据都是可序列化的)。而且这里面的单词肯定有重复的,不重复统计干啥。map函数的从输入到输出就是对数据进行封装处理。*/  
  22.        extends Mapper<Object, Text, Text, IntWritable>{  
  23.     /*定义两个变量,一个是value的,一个是key的。输出的时候就用这两个。*/  
  24.     private final static IntWritable one = new IntWritable(1);  
  25.     private Text word = new Text();  
  26.     /*到了实现mapper类里面方法的时候了。map是父类的方法。加@Override更保险,如果这个地方你不小心弄错了,变成自己新的方法,map过程就不会执行了。参数列表中前两个对于map的输入,跟map类的前两个一样。Context是用来封装好输出,交给reduce处理的。*/  
  27.     public void map(Object key, Text value, Context context  
  28.                     ) throws IOException, InterruptedException {  
  29.     /*这里使用到了字符串的分词器,以空格分割,是按照我们输入的每行数据的格式。*/  
  30.       StringTokenizer itr = new StringTokenizer(value.toString());  
  31.       /*分词后的数据很像一个迭代器,每次取出一个词,封装成Text类型的key,IntWritable类型的value对输出,这里value是1*/  
  32.       while (itr.hasMoreTokens()) {  
  33.         word.set(itr.nextToken());  
  34.         context.write(word, one);  
  35.       }  
  36.     }  
  37.   }  
  38.  /*reduce的作用就是累加。首先,reduce会收集到具有相同key的key,value对,单词相同的就可以累加,有多少相同的就有多少个1,累加起来就是词数。继承reducer类,四个参数,前两个是map的输出作为reduce的输入,后两个是reduce的输出,输出key是单词,value词数。*/  
  39.   public static class IntSumReducer   
  40.        extends Reducer<Text,IntWritable,Text,IntWritable> {  
  41.     private IntWritable result = new IntWritable();  
  42.     /*参数列表类型照搬上面的前两个,有点区别,这里输入的是具有相同key的键值对的集合,因此是可迭代类型,每个元素的类型是intwritable的。Context作用一样*/  
  43.     public void reduce(Text key, Iterable<IntWritable> values,   
  44.                        Context context  
  45.                        ) throws IOException, InterruptedException {  
  46.       /*int sum是用来在这个函数里做累加的,都是相同的key的集合,把value一个一个加起来。最后输出的肯定是序列化的intwritable类型的result,result调用set方法赋值*/  
  47.       int sum = 0;  
  48.       for (IntWritable val : values) {  
  49.         sum += val.get();  
  50.       }  
  51.       result.set(sum);  
  52.       context.write(key, result);  
  53.     }  
  54.   }  
  55.    
  56.   public static void main(String[] args) throws Exception {  
  57.    /*conf可以理解为在hadoop的conf文件一样的作用。在运行的时候,需要将conf的配置文件拖到src的目录下,否则会报错的,eclipse读取不到hadoop的配置文件下的东西,自身目录下的文件可以读取*/  
  58.     Configuration conf = new Configuration();  
  59.    /*输入的参数,input和output目录里的统计用的文件,在eclipse设置。*/  
  60.     String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();  
  61.     if (otherArgs.length < 2) {  
  62.       System.err.println("Usage: wordcount <in> [<in>...] <out>");  
  63.       System.exit(2);  
  64.     }  
  65.    /*要运行这个mapreduce的作业就要用到job了。提交这个job就要告诉hadoop我们的jar包,我们用的map和reduce的类是什么,hadoop才会按照我们的指示去执行工作。combiner是一种优化的手段,但并不是都有效,比如求平均值。我们最后得到的结果也是在job的setoutputkeyclass和setoutputvalueclass里面设置。这个与reduce的输出是对应的,写入到part-r-xxxx的文件里。*/  
  66.     Job job = Job.getInstance(conf, "word count");  
  67.     job.setJarByClass(WordCount.class);  
  68.     job.setMapperClass(TokenizerMapper.class);  
  69.     job.setCombinerClass(IntSumReducer.class);  
  70.     job.setReducerClass(IntSumReducer.class);  
  71.     job.setOutputKeyClass(Text.class);  
  72.     job.setOutputValueClass(IntWritable.class);  
  73.     for (int i = 0; i < otherArgs.length - 1; ++i) {  
  74.       FileInputFormat.addInputPath(job, new Path(otherArgs[i]));  
  75.     }  
  76.     FileOutputFormat.setOutputPath(job,  
  77.       new Path(otherArgs[otherArgs.length - 1]));  
  78.    /*waitforcomletion的作用就是提交这个作业执行,成功返回1,并退出*/  
  79.     System.exit(job.waitForCompletion(true) ? 0 : 1);  
  80.   }  
  81. }  


现在还有一些问题需要处理。
1、
在map之前,hadoop就进行了一次key,value的划分,按行划分的。但是,如果一个文件里面,给的格式是一个key后面跟着一个value的话,怎么处理?这里,默认是text类型,这个类型是以行\n分割,它的键值是<int(行偏移),text(行的文本)>,在处理wordcount,这种划分的比较适合。而例如文本中给定了key,value的数据,使用keyvalueinputformat的类型进行文本分割比较好。这里默认的分隔符是\t。使用下面的命令显示指定预处理的方法。
job.setInputFormatClass(KeyValueTextInputFormat.class);
2、接着上面的,如果分隔符不是/t,而是逗号,这些预处理怎么处理。hadoop提供了解决方案,设置分隔符就可以了。命令要在job实例化之前。
conf.set("mapreduce.input.keyvaluelinerecordreader.key.value.separator", ",");
3、新版本使用的是iteratable来收集键值对集而不是 iterator,旧版本使用的是output.collect()的方法来实现键值对的写入传递,新版本使用的context来负责写入。这里就不列出1.x的代码了,网上很多很多的例子。

第二:hadoop实战专利引用代码解析


第一个例子说完,就可以自己比较容易的去修改hadoop实战中的例子了。hadoop实战中专利数据的处理用到的是hadoop1.x的。这是部分数据的截取,第一列是专利列,第二列是第一列专利所引用到的其它专利。接下来我们要统计,第二列的每个专利,都有那些在第一列中的专利引用了,value采用逗号分隔并追加的方法。key和value以空格隔开。
资源地址:http://www.nber.org/patents/ 下的cite75_99.txt

输入文件
[plain] view plain copy
 print?
  1. cit"CITING","CITED"  
  2. 3858241,956203  
  3. 3858241,1324234  
  4. 3858241,3398406  
  5. 3858241,3557384  
  6. 3858241,3634889  
  7. 3858242,1515701  
  8. 3858242,3319261  
  9. 3858242,3668705  




输出文件

[plain] view plain copy
 print?
  1. "CITED" "CITING"  
  2. 1       3964859,4647229  
  3. 10000   4539112  
  4. 100000  5031388  
  5. 1000006 4714284  
  6. 1000007 4766693  
  7. 1000011 5033339  
  8. 1000017 3908629  
  9. 1000026 4043055  
  10. 1000033 4190903,4975983  
  11. 1000043 4091523  
  12. 1000044 4082383,4055371  
  13. 1000045 4290571  
  14. 1000046 5525001,5918892  



我们把它的代码改成2.x的,1.x的代码不说了,直接上2.x的。




[java] view plain copy
 print?
  1. import java.io.IOException;  
  2. import java.util.Iterator;  
  3. import java.util.StringTokenizer;  
  4.   
  5.   
  6. import org.apache.hadoop.conf.Configuration;  
  7. import org.apache.hadoop.fs.Path;  
  8. import org.apache.hadoop.io.IntWritable;  
  9. import org.apache.hadoop.io.Text;  
  10. import org.apache.hadoop.mapred.JobClient;  
  11. import org.apache.hadoop.mapreduce.Job;  
  12. import org.apache.hadoop.mapreduce.Mapper;  
  13. import org.apache.hadoop.mapreduce.Reducer;  
  14. import org.apache.hadoop.mapreduce.Reducer.Context;  
  15. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  16. import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;  
  17. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  18. import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;  
  19. import org.apache.hadoop.util.GenericOptionsParser;  
  20.   
  21.   
  22. import test.WordCount.IntSumReducer;  
  23. import test.WordCount.TokenizerMapper;  
  24.   
  25.   
  26. public class patent {  
  27.     public static class Map   
  28.     extends Mapper<Text, Text, Text, Text>{  
  29.      /*直接使用文本来操作,很方便。这里的输入和输出我们都使用上面说的KeyValueTextInputFormat文本预处理。逗号分隔conf.set("mapreduce.input.keyvaluelinerecordreader.key.value.separator", ","),处理的东西都变成文本,又不统计个数。*/  
  30.      public void map(Text key, Text value, Context context  
  31.                      ) throws IOException, InterruptedException {  
  32.          /*这里的map唯一需要做的就是把key和value颠倒过来,我们的目的就是统计被引用的专利有哪些第一列的专利引用,这里的key,value反过来了*/  
  33.          context.write(value,key);  
  34.      }  
  35.     }  
  36.       
  37.     public static class Reduce   
  38.         extends Reducer<Text,Text,Text,Text> {  
  39.      /*reducer的方法,第一个是key,唯一键,第二个是value,有很多,之前也说了,reduce收集的是具有相同key的键值对的集合,集合进行了封装,类似于<key1 value1,value2,value3>*/  
  40.      public void reduce(Text key, Iterable<Text> values,   
  41.                         Context context  
  42.                         ) throws IOException, InterruptedException {  
  43.       /*接下来,就是把value的字符串连接成以逗号分隔,value1,value2,value3这种类型的形式,这里的转化成了iterator,因为想要next方法来获取数据,iterator比较好操作,这里的操作有点倒退了,毕竟1.x使用的就是iterator,但在传输上,iterator更具有优势。while循环就不说了,很简单。*/  
  44.       String csv="";  
  45.       Iterator<Text> iterator=values.iterator();  
  46.       while (iterator.hasNext()){  
  47.           if(csv.length()>0)csv+=",";  
  48.           csv+=iterator.next().toString();  
  49.       }  
  50.       context.write(key, new Text(csv));  
  51.      }  
  52.     }  
  53.       
  54.     public static void main(String[] args) throws Exception {  
  55.      Configuration conf = new Configuration();  
  56.     /*设置每一行的key,value划分都是以逗号分隔*/  
  57.      conf.set("mapreduce.input.keyvaluelinerecordreader.key.value.separator"",");  
  58.      Job job = Job.getInstance(conf, "patent");  
  59.      job.setJarByClass(patent.class);  
  60.      job.setMapperClass(Map.class);  
  61.     /*输入的文本使用的是逗号分隔key,value的方法,输出是text,这个设不设都可以*/  
  62.      job.setInputFormatClass(KeyValueTextInputFormat.class);  
  63.      job.setOutputFormatClass(TextOutputFormat.class);  
  64.      job.setReducerClass(Reduce.class);  
  65.      job.setOutputKeyClass(Text.class);  
  66.      job.setOutputValueClass(Text.class);  
  67.     /*沿用了1.x的方法,个人觉得使用这个看起来比2.x那个简洁多*/  
  68.      Path in = new Path(args[0]);  
  69.      Path out = new Path(args[1]);  
  70.      FileInputFormat.setInputPaths(job, in);  
  71.      FileOutputFormat.setOutputPath(job, out);  
  72.     /*执行,输出结果上面已经给了*/  
  73.      System.exit(job.waitForCompletion(true)?0:1);  
  74.     }  
  75. }  

额外的说明
1、
这里说一下输入的参数,这个是在eclipse里面设置的。如果你愿意可以吧args[]直接替换成路径。
/user/root/test/cite75_99.txt /user/root/output
输入和输出之间有个空格。因为hadoop2.x使用的是/user/username/file的格式,这里就在hdfs里面创建了/user/root/的目录。
2、扯一下文件分片的问题。文件大小是251M,默认的datanode一个片的大小是128M,1.x是64M吧,因此250M的文件会被分成2片。可以通过http://localhost:50070/的utilities菜单栏的browse the file system,输入/user/root/test-----这是我存放文件的目录,可以看到文件的详细信息。存放也是按块均匀的(我的集群是2个datanode,因此一个128,一个122,它显示大小是以字节为单位的*1024*1024)存放在各个datanode上,mapred.min.split.size可以设定大小。
3、eclipse运行的时候需要复制配置文件到src目录下,例如log4j.properties就是用来显示日志的.关于日志文件。有两种,一个是log一个是out。log是通过log4j记录,记录大部分的日志信息。out是标准输出和标准错误日志,少量记录。
日志的命名规则=框架名称-用户名-进程名-主机名-日志格式后缀
可以开一个终端使用命令:tail -f 日志名-----------会停顿在结尾,当有新日志刷新的时候,会滚动显示
MapReduce程序日志分为历史作业日志和container日志
历史作业日志
包含多少个Map,用了多少个Reduce,作业提交时间,作业启动时间,作业完成时间等
container日志---yarn
ApplicationMaster日志和普通的task日志 ${HADOOP_HOME}/logs/userlogs
Application Master的路径是/tmp/hadoop-yarn/staging下,这里的/tmp/是hdfs文件系统的目录,通过web可以查看
/tmp/hadoop-yarn/staging/history/done/年/月/日/  按日期存放的
4、静态内部map和reduce类
好处是简化代码管理,这些内部类与主类独立,不交互,在执行时,采用不同的jvm的各类节点复制并运行map reduce,其它作业类在客户机上执行。
5、 Job job_local1970859078_0001 running in uber mode : false
打印的日志会有这么一句话,uber mode如果设置为true则使用一台jvm执行,伪分布测试小数据的话会非常的快,毕竟,开的JVM太多会有额外的开销,实际运行是在不同节点开启多个jvm同时运行的。通过把mapreduce.job.ubertask.enable设置为true可以开启。
6、 我们常说namenode存储的是元数据,hadoop的fsimage元数据存放在namenode定义的目录中,用来联系datanode
fsimage保存了最新的元数据检查点。
edits保存自最新检查点后的命名空间的变化,secondNamenode的协助作用体现在,会周期性的合并为fsimage
默认这两文件都放在namenode文件夹下面,可以分开设置并存放。
dfs.namenode.name.dir和dfs.namenode.edits.dir是设置存放目录的
dfs.namenode.checkpoint.dir是设置secondarynamenode进行合并的目录
7、 aggregation日志聚合
日志聚集是YARN提供的日志中央化管理功能,它能将运行完成的Container/任务日志上传到HDFS上,从而减轻NodeManager负载,且提供一个中央化存储和分析机制。默认情况下,Container/任务日志存在在各个NodeManager上。日志聚合的目录是/tmp/logs
yarn-site.xml可以进行配置。
开启的话是yarn.log-aggregation-enable-------true
聚合的默认日期 yarn.log-aggregation.retain-seconds----------------默认是以S为单位
8、 eclipse运行hadoop

我的出现了几个问题。在把core、hdfs和log配置文件拷到src目录下的时候,是可以运行在Hadoop上的。运行在yarn上需要四个配置文件都拷贝,但是还是出现了找不到内部类的错误。后来直接将工程导出为jar包,使用/bin/yarn命令运行是没有问题的。好像是eclipse缺失yarn的jar,暂时未解决。

0 0
原创粉丝点击