(转)hadoop多文件格式输入

hadoop多文件格式输入,一般可以使用MultipleInputs类指定不同的输入文件路径以及输入文件格式

原文:http://blog.csdn.net/fansy1990/article/details/26267637

版本:

CDH5.0.0 (hdfs:2.3,mapreduce:2.3,yarn:2.3)

hadoop多文件格式输入,一般可以使用MultipleInputs类指定不同的输入文件路径以及输入文件格式。

比如现在有如下的需求:

现有两份数据:

phone:

 

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. 123,good number  
  2. 124,common number  
  3. 125,bad number  

user:

 

 

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. zhangsan,123  
  2. lisi,124  
  3. wangwu,125  


现在需要把user和phone按照phone number连接起来,得到下面的结果:

 

 

[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. zhangsan,123,good number  
  2. lisi,123,common number  
  3. wangwu,125,bad number  

那么就可以使用MultipleInputs来操作,这里把user和phone上传到hdfs目录中,分别是/multiple/user/user , /multiple/phone/phone。

 

设计的MultipleDriver如下:

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. package multiple.input;  
  2.   
  3. import org.apache.hadoop.conf.Configuration;  
  4. import org.apache.hadoop.conf.Configured;  
  5. import org.apache.hadoop.fs.Path;  
  6. import org.apache.hadoop.io.NullWritable;  
  7. import org.apache.hadoop.io.Text;  
  8. import org.apache.hadoop.mapreduce.Job;  
  9. import org.apache.hadoop.mapreduce.lib.input.MultipleInputs;  
  10. import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;  
  11. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  12. import org.apache.hadoop.util.Tool;  
  13. import org.apache.hadoop.util.ToolRunner;  
  14. //import org.slf4j.Logger;  
  15. //import org.slf4j.LoggerFactory;  
  16. /** 
  17.  * input1(/multiple/user/user): 
  18.  * username,user_phone 
  19.  *   
  20.  * input2(/multiple/phone/phone): 
  21.  *  user_phone,description  
  22.  *   
  23.  * output: username,user_phone,description 
  24.  *  
  25.  * @author fansy 
  26.  * 
  27.  */  
  28. public class MultipleDriver extends Configured implements Tool{  
  29. //  private  Logger log = LoggerFactory.getLogger(MultipleDriver.class);  
  30.       
  31.     private String input1=null;  
  32.     private String input2=null;  
  33.     private String output=null;  
  34.     private String delimiter=null;  
  35.       
  36.     public static void main(String[] args) throws Exception {  
  37.         Configuration conf=new Configuration();  
  38. //      conf.set("fs.defaultFS", "hdfs://node33:8020");    
  39. //        conf.set("mapreduce.framework.name", "yarn");    
  40. //        conf.set("yarn.resourcemanager.address", "node33:8032");   
  41.           
  42.         ToolRunner.run(conf, new MultipleDriver(), args);  
  43.     }  
  44.   
  45.     @Override  
  46.     public int run(String[] arg0) throws Exception {  
  47.         configureArgs(arg0);  
  48.         checkArgs();  
  49.           
  50.         Configuration conf= getConf();  
  51.         conf.set("delimiter", delimiter);  
  52.          @SuppressWarnings("deprecation")  
  53.         Job job = new Job(conf, "merge user and phone information ");  
  54.         job.setJarByClass(MultipleDriver.class);  
  55.   
  56.         job.setReducerClass(MultipleReducer.class);  
  57.         job.setMapOutputKeyClass(Text.class);  
  58.         job.setMapOutputValueClass(FlagStringDataType.class);  
  59.         job.setOutputKeyClass(Text.class);  
  60.         job.setOutputValueClass(NullWritable.class);  
  61.           
  62.         job.setNumReduceTasks(1);  
  63.         MultipleInputs.addInputPath(job, new Path(input1), TextInputFormat.class, Multiple1Mapper.class);  
  64.         MultipleInputs.addInputPath(job, new Path(input2), TextInputFormat.class, Multiple2Mapper.class);  
  65.         FileOutputFormat.setOutputPath(job, new Path(output));  
  66.           
  67.         int res = job.waitForCompletion(true) ? 0 : 1;  
  68.         return res;  
  69.     }  
  70.       
  71.   
  72.     /** 
  73.      * check the args  
  74.      */  
  75.     private void checkArgs() {  
  76.         if(input1==null||"".equals(input1)){  
  77.             System.out.println("no user input...");  
  78.             printUsage();  
  79.             System.exit(-1);  
  80.         }  
  81.         if(input2==null||"".equals(input2)){  
  82.             System.out.println("no phone input...");  
  83.             printUsage();  
  84.             System.exit(-1);  
  85.         }  
  86.         if(output==null||"".equals(output)){  
  87.             System.out.println("no output...");  
  88.             printUsage();  
  89.             System.exit(-1);  
  90.         }  
  91.         if(delimiter==null||"".equals(delimiter)){  
  92.             System.out.println("no delimiter...");  
  93.             printUsage();  
  94.             System.exit(-1);  
  95.         }  
  96.       
  97.     }  
  98.   
  99.     /** 
  100.      * configuration the args 
  101.      * @param args 
  102.      */  
  103.     private void configureArgs(String[] args) {  
  104.         for(int i=0;i<args.length;i++){  
  105.             if("-i1".equals(args[i])){  
  106.                 input1=args[++i];  
  107.             }  
  108.             if("-i2".equals(args[i])){  
  109.                 input2=args[++i];  
  110.             }  
  111.               
  112.             if("-o".equals(args[i])){  
  113.                 output=args[++i];  
  114.             }  
  115.               
  116.             if("-delimiter".equals(args[i])){  
  117.                 delimiter=args[++i];  
  118.             }  
  119.               
  120.         }  
  121.     }  
  122.     public static void printUsage(){  
  123.         System.err.println("Usage:");  
  124.         System.err.println("-i1 input \t user data path.");  
  125.         System.err.println("-i2 input \t phone data path.");  
  126.         System.err.println("-o output \t output data path.");  
  127.         System.err.println("-delimiter  data delimiter , default is comma  .");  
  128.     }  
  129. }  


这里指定两个mapper和一个reducer,两个mapper分别对应处理user和phone的数据,分别如下:

 

mapper1(处理user数据):

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. package multiple.input;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.hadoop.io.LongWritable;  
  6. import org.apache.hadoop.io.Text;  
  7. import org.apache.hadoop.mapreduce.Mapper;  
  8. import org.slf4j.Logger;  
  9. import org.slf4j.LoggerFactory;  
  10. /** 
  11.  * input : 
  12.  * username,phone 
  13.  *  
  14.  * output: 
  15.  * <key,value>  --> <[phone],[0,username]> 
  16.  * @author fansy 
  17.  * 
  18.  */  
  19. public class Multiple1Mapper extends Mapper<LongWritable,Text,Text,FlagStringDataType>{  
  20.     private  Logger log = LoggerFactory.getLogger(Multiple1Mapper.class);  
  21.     private String delimiter=null// default is comma  
  22.     @Override  
  23.     public void setup(Context cxt){  
  24.         delimiter= cxt.getConfiguration().get("delimiter"",");  
  25.         log.info("This is the begin of Multiple1Mapper");  
  26.     }   
  27.       
  28.     @Override  
  29.     public void map(LongWritable key,Text value,Context cxt) throws IOException,InterruptedException{  
  30.         String info= new String(value.getBytes(),"UTF-8");  
  31.         String[] values = info.split(delimiter);  
  32.         if(values.length!=2){  
  33.             return;  
  34.         }  
  35.         log.info("key-->"+values[1]+"=========value-->"+"[0,"+values[0]+"]");  
  36.         cxt.write(new Text(values[1]), new FlagStringDataType(0,values[0]));  
  37.     }  
  38. }  


mapper2(处理phone数据):

 

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. package multiple.input;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.hadoop.io.LongWritable;  
  6. import org.apache.hadoop.io.Text;  
  7. import org.apache.hadoop.mapreduce.Mapper;  
  8. import org.slf4j.Logger;  
  9. import org.slf4j.LoggerFactory;  
  10. /** 
  11.  * input : 
  12.  * phone,description 
  13.  *  
  14.  * output: 
  15.  * <key,value>  --> <[phone],[1,description]> 
  16.  * @author fansy 
  17.  * 
  18.  */  
  19. public class Multiple2Mapper extends Mapper<LongWritable,Text,Text,FlagStringDataType>{  
  20.     private  Logger log = LoggerFactory.getLogger(Multiple2Mapper.class);  
  21.     private String delimiter=null// default is comma  
  22.     @Override  
  23.     public void setup(Context cxt){  
  24.         delimiter= cxt.getConfiguration().get("delimiter"",");  
  25.         log.info("This is the begin of Multiple2Mapper");  
  26.     }   
  27.       
  28.     @Override  
  29.     public void map(LongWritable key,Text value,Context cxt) throws IOException,InterruptedException{  
  30.         String[] values= value.toString().split(delimiter);  
  31.         if(values.length!=2){  
  32.             return;  
  33.         }  
  34.         log.info("key-->"+values[0]+"=========value-->"+"[1,"+values[1]+"]");  
  35.         cxt.write(new Text(values[0]), new FlagStringDataType(1,values[1]));  
  36.     }  
  37. }  

这里的FlagStringDataType是自定义的:

 

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. package multiple.input;  
  2.   
  3. import java.io.DataInput;  
  4. import java.io.DataOutput;  
  5. import java.io.IOException;  
  6.   
  7. import org.apache.hadoop.io.WritableComparable;  
  8. import org.slf4j.Logger;  
  9. import org.slf4j.LoggerFactory;  
  10.   
  11. import com.google.common.primitives.Ints;  
  12.   
  13. public class FlagStringDataType implements WritableComparable<FlagStringDataType> {  
  14.     private  Logger log = LoggerFactory.getLogger(FlagStringDataType.class);  
  15.   private String value;  
  16.   private int flag;  
  17.   public FlagStringDataType() {  
  18.   }  
  19.   
  20.   public FlagStringDataType(int flag,String value) {  
  21.     this.value = value;  
  22.     this.flag=flag;  
  23.   }  
  24.   
  25.   public String get() {  
  26.     return value;  
  27.   }  
  28.   
  29.   public void set(String value) {  
  30.     this.value = value;  
  31.   }  
  32.   
  33.   @Override  
  34.   public boolean equals(Object other) {  
  35.     return other != null && getClass().equals(other.getClass())   
  36.             && ((FlagStringDataType) other).get() == value  
  37.             &&((FlagStringDataType) other).getFlag()==flag;  
  38.   }  
  39.   
  40.   @Override  
  41.   public int hashCode() {  
  42.     return Ints.hashCode(flag)+value.hashCode();  
  43.   }  
  44.   
  45.   @Override  
  46.   public int compareTo(FlagStringDataType other) {  
  47.        
  48.     if (flag >= other.flag) {  
  49.       if (flag > other.flag) {  
  50.         return 1;  
  51.       }  
  52.     } else {  
  53.       return -1;  
  54.     }  
  55.     return value.compareTo(other.value);  
  56.   }  
  57.   
  58.   @Override  
  59.   public void write(DataOutput out) throws IOException {  
  60.     log.info("in write()::"+"flag:"+flag+",vlaue:"+value);  
  61.     out.writeInt(flag);  
  62.     out.writeUTF(value);  
  63.   }  
  64.   
  65.   @Override  
  66.   public void readFields(DataInput in) throws IOException {  
  67.       log.info("in read()::"+"flag:"+flag+",vlaue:"+value);  
  68.       flag=in.readInt();  
  69.       value = in.readUTF();  
  70.       log.info("in read()::"+"flag:"+flag+",vlaue:"+value);  
  71.   }  
  72.   
  73. public int getFlag() {  
  74.     return flag;  
  75. }  
  76.   
  77. public void setFlag(int flag) {  
  78.     this.flag = flag;  
  79. }  
  80.   
  81. public String toString(){  
  82.     return flag+":"+value;  
  83. }  
  84.   
  85. }  


这个自定义类,使用一个flag来指定是哪个数据,而value则对应是其值。这样做的好处是在reduce端可以根据flag的值来判断其输出位置,这种设计方式可以对多种输入的整合有很大帮助,在mahout中也可以看到这样的设计。

 

reducer(汇总输出数据):

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. package multiple.input;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.hadoop.io.NullWritable;  
  6. import org.apache.hadoop.io.Text;  
  7. import org.apache.hadoop.mapreduce.Reducer;  
  8. import org.slf4j.Logger;  
  9. import org.slf4j.LoggerFactory;  
  10.   
  11. public class MultipleReducer extends Reducer<Text,FlagStringDataType,Text,NullWritable>{  
  12.     private  Logger log = LoggerFactory.getLogger(MultipleReducer.class);  
  13.     private String delimiter=null// default is comma  
  14.     @Override  
  15.     public void setup(Context cxt){  
  16.         delimiter= cxt.getConfiguration().get("delimiter"",");  
  17.     }   
  18.     @Override  
  19.     public void reduce(Text key, Iterable<FlagStringDataType> values,Context cxt) throws IOException,InterruptedException{  
  20.         log.info("================");  
  21.         log.info("         =======");  
  22.         log.info("              ==");  
  23.         String[] value= new String[3];  
  24.         value[2]=key.toString();  
  25.         for(FlagStringDataType v:values){  
  26.             int index= v.getFlag();  
  27.             log.info("index:"+index+"-->value:"+v.get());  
  28.             value[index]= v.get();  
  29.         }  
  30.         log.info("              ==");  
  31.         log.info("         =======");  
  32.         log.info("================");  
  33.         cxt.write(new Text(value[2]+delimiter+value[0]+delimiter+value[1]),NullWritable.get());  
  34.     }  
  35. }  


这样设计的好处是,可以针对不同的输入数据采取不同的逻辑处理,而且不同的输入数据可以是序列文件的格式。

 

下面介绍一种方式和上面的比,略有不足,但是可以借鉴。

首先是Driver:

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. package multiple.input;  
  2.   
  3. import org.apache.hadoop.conf.Configuration;  
  4. import org.apache.hadoop.conf.Configured;  
  5. import org.apache.hadoop.fs.Path;  
  6. import org.apache.hadoop.io.NullWritable;  
  7. import org.apache.hadoop.io.Text;  
  8. import org.apache.hadoop.mapreduce.Job;  
  9. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  10. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  11. import org.apache.hadoop.util.Tool;  
  12. import org.apache.hadoop.util.ToolRunner;  
  13. //import org.slf4j.Logger;  
  14. //import org.slf4j.LoggerFactory;  
  15. /** 
  16.  * input1(/multiple/user/user): 
  17.  * username,user_phone 
  18.  *   
  19.  * input2(/multiple/phone/phone): 
  20.  *  user_phone,description  
  21.  *   
  22.  * output: username,user_phone,description 
  23.  *  
  24.  * @author fansy 
  25.  * 
  26.  */  
  27. public class MultipleDriver2 extends Configured implements Tool{  
  28. //  private  Logger log = LoggerFactory.getLogger(MultipleDriver.class);  
  29.       
  30.     private String input1=null;  
  31.     private String input2=null;  
  32.     private String output=null;  
  33.     private String delimiter=null;  
  34.       
  35.     public static void main(String[] args) throws Exception {  
  36.         Configuration conf=new Configuration();  
  37. //      conf.set("fs.defaultFS", "hdfs://node33:8020");    
  38. //        conf.set("mapreduce.framework.name", "yarn");    
  39. //        conf.set("yarn.resourcemanager.address", "node33:8032");   
  40.           
  41.         ToolRunner.run(conf, new MultipleDriver2(), args);  
  42.     }  
  43.   
  44.     @Override  
  45.     public int run(String[] arg0) throws Exception {  
  46.         configureArgs(arg0);  
  47.         checkArgs();  
  48.           
  49.         Configuration conf= getConf();  
  50.         conf.set("delimiter", delimiter);  
  51.          @SuppressWarnings("deprecation")  
  52.         Job job = new Job(conf, "merge user and phone information ");  
  53.         job.setJarByClass(MultipleDriver2.class);  
  54.         job.setMapperClass(MultipleMapper.class);  
  55.         job.setReducerClass(MultipleReducer.class);  
  56.         job.setMapOutputKeyClass(Text.class);  
  57.         job.setMapOutputValueClass(FlagStringDataType.class);  
  58.         job.setOutputKeyClass(Text.class);  
  59.         job.setOutputValueClass(NullWritable.class);  
  60.           
  61.         job.setNumReduceTasks(1);  
  62.         FileInputFormat.addInputPath(job, new Path(input1));  
  63.         FileInputFormat.addInputPath(job, new Path(input2));  
  64.         FileOutputFormat.setOutputPath(job, new Path(output));  
  65.           
  66.         int res = job.waitForCompletion(true) ? 0 : 1;  
  67.         return res;  
  68.     }  
  69.       
  70.   
  71.     /** 
  72.      * check the args  
  73.      */  
  74.     private void checkArgs() {  
  75.         if(input1==null||"".equals(input1)){  
  76.             System.out.println("no user input...");  
  77.             printUsage();  
  78.             System.exit(-1);  
  79.         }  
  80.         if(input2==null||"".equals(input2)){  
  81.             System.out.println("no phone input...");  
  82.             printUsage();  
  83.             System.exit(-1);  
  84.         }  
  85.         if(output==null||"".equals(output)){  
  86.             System.out.println("no output...");  
  87.             printUsage();  
  88.             System.exit(-1);  
  89.         }  
  90.         if(delimiter==null||"".equals(delimiter)){  
  91.             System.out.println("no delimiter...");  
  92.             printUsage();  
  93.             System.exit(-1);  
  94.         }  
  95.       
  96.     }  
  97.   
  98.     /** 
  99.      * configuration the args 
  100.      * @param args 
  101.      */  
  102.     private void configureArgs(String[] args) {  
  103.         for(int i=0;i<args.length;i++){  
  104.             if("-i1".equals(args[i])){  
  105.                 input1=args[++i];  
  106.             }  
  107.             if("-i2".equals(args[i])){  
  108.                 input2=args[++i];  
  109.             }  
  110.               
  111.             if("-o".equals(args[i])){  
  112.                 output=args[++i];  
  113.             }  
  114.               
  115.             if("-delimiter".equals(args[i])){  
  116.                 delimiter=args[++i];  
  117.             }  
  118.               
  119.         }  
  120.     }  
  121.     public static void printUsage(){  
  122.         System.err.println("Usage:");  
  123.         System.err.println("-i1 input \t user data path.");  
  124.         System.err.println("-i2 input \t phone data path.");  
  125.         System.err.println("-o output \t output data path.");  
  126.         System.err.println("-delimiter  data delimiter , default is comma  .");  
  127.     }  
  128. }  


这里添加路径直接使用FileInputFormat添加输入路径,这样的话,针对不同的输入数据的不同业务逻辑可以在mapper中先判断目前正在处理的是那个数据,然后根据其路径来进行相应的业务逻辑处理:

 

 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
 
  1. package multiple.input;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import org.apache.hadoop.io.LongWritable;  
  6. import org.apache.hadoop.io.Text;  
  7. import org.apache.hadoop.mapreduce.InputSplit;  
  8. import org.apache.hadoop.mapreduce.Mapper;  
  9. import org.apache.hadoop.mapreduce.lib.input.FileSplit;  
  10. /** 
  11.  * input1 : 
  12.  * username,phone 
  13.  *  
  14.  * input2 
  15.  * phone,description 
  16.  *  
  17.  * output: 
  18.  * <key,value>  --> <[phone],[0,username]> 
  19.  * <key,value>  --> <[phone],[1,description]> 
  20.  * @author fansy 
  21.  * 
  22.  */  
  23. public class MultipleMapper extends Mapper<LongWritable,Text,Text,FlagStringDataType>{  
  24.       
  25.     private String delimiter=null// default is comma  
  26.     private boolean flag=false;  
  27.     @Override  
  28.     public void setup(Context cxt){  
  29.         delimiter= cxt.getConfiguration().get("delimiter"",");  
  30.         InputSplit input=cxt.getInputSplit();    
  31.         String filename=((FileSplit) input).getPath().getParent().getName();  
  32.         if("user".equals(filename)){  
  33.             flag=true;  
  34.         }  
  35.     }   
  36.       
  37.     @Override  
  38.     public void map(LongWritable key,Text value,Context cxt) throws IOException,InterruptedException{  
  39.         String[] values= value.toString().split(delimiter);  
  40.         if(values.length!=2){  
  41.             return;  
  42.         }  
  43.         if(flag){  
  44.             cxt.write(new Text(values[1]), new FlagStringDataType(0,values[0]));  
  45.         }else{  
  46.             cxt.write(new Text(values[0]), new FlagStringDataType(1,values[1]));  
  47.         }  
  48.     }  
  49. }  


总体来说,这种处理方式其实是不如第一种的,在每个map函数中都需要进行判断,比第一种多了很多操作;同时,针对不同的序列文件,这种方式处理不了(Key、value的类型不一样的情况下)。所以针对多文件格式的输入,最好还是使用第一种方式。

 

 

 

分享,成长,快乐

转载请注明blog地址:http://blog.csdn.net/fansy1990

猜你喜欢

转载自zhouchaofei2010.iteye.com/blog/2094974