hadoop map(分片)数量确定

之前学习hadoop的时候,一直希望可以调试hadoop源码,可是一直没找到有效的方法,今天在调试矩阵乘法的时候发现了调试的方法,所以在这里记录下来。

1)事情的起因是想在一个Job里设置map的数量(虽然最终的map数量是由分片决定的),在hadoop1.2.1之前,设置方法是:

job.setNumMapTasks()

不过,hadoop1.2.1没有了这个方法,只保留了设置reduce数量的方法。继续搜索资料,发现有同学提供了另外一种方法,就是使用configuration设置,设置方式如下:

conf.set("mapred.map.tasks",5);//设置5个map

按照上述方法设置之后,还是没有什么效果,控制分片数量的代码如下():

goalSize=totalSize/(numSplits==0?1:numSplits)
//totalSize是输入数据文件的大小,numSplits是用户设置的map数量,就是按照用户自己
//的意愿,每个分片的大小应该是goalSize
minSize=Math.max(job.getLong("mapred.min.split.size",1),minSplitSize)
//hadoop1.2.1中mapred-default.xml文件中mapred.min.split.size=0,所以job.getLong("mapred.min.split.size",1)=0,而minSplitSize是InputSplit中的一个数据成员,在File//Split中值为1.所以minSize=1,其目的就是得到配置中的最小值。
splitSize=Math.max(minSize,Math.min(goalSize,blockSize))
//真正的分片大小就是取按照用户设置的map数量计算出的goalSize和块大小blockSize中最小值(这是为了是分片不会大于一个块大小,有利于本地化计算),并且又比minSize大的值。

其实,这是hadoop1.2.1之前的生成分片的方式,所以即使设置了map数量也不会有什么实际效果。

2)新版API(hadoop1.2.1)中计算分片的代码如下所示:

 1  public List<InputSplit> getSplits(JobContext job) throws IOException {
 2         long minSize = Math.max(this.getFormatMinSplitSize(), getMinSplitSize(job));
 3         long maxSize = getMaxSplitSize(job);
 4         ArrayList splits = new ArrayList();
 5         List files = this.listStatus(job);
 6         Iterator i$ = files.iterator();
 7 
 8         while(true) {
 9             while(i$.hasNext()) {
10                 FileStatus file = (FileStatus)i$.next();
11                 Path path = file.getPath();
12                 FileSystem fs = path.getFileSystem(job.getConfiguration());
13                 long length = file.getLen();
14                 BlockLocation[] blkLocations = fs.getFileBlockLocations(file, 0L, length);
15                 if(length != 0L && this.isSplitable(job, path)) {
16                     long blockSize = file.getBlockSize();
17                     long splitSize = this.computeSplitSize(blockSize, minSize, maxSize);
18 
19                     long bytesRemaining;
20                     for(bytesRemaining = length; (double)bytesRemaining / (double)splitSize > 1.1D; bytesRemaining -= splitSize) {
21                         int blkIndex = this.getBlockIndex(blkLocations, length - bytesRemaining);
22                         splits.add(new FileSplit(path, length - bytesRemaining, splitSize, blkLocations[blkIndex].getHosts()));
23                     }
24 
25                     if(bytesRemaining != 0L) {
26                         splits.add(new FileSplit(path, length - bytesRemaining, bytesRemaining, blkLocations[blkLocations.length - 1].getHosts()));
27                     }
28                 } else if(length != 0L) {
29                     splits.add(new FileSplit(path, 0L, length, blkLocations[0].getHosts()));
30                 } else {
31                     splits.add(new FileSplit(path, 0L, length, new String[0]));
32                 }
33             }
34 
35             job.getConfiguration().setLong("mapreduce.input.num.files", (long)files.size());
36             LOG.debug("Total # of splits: " + splits.size());
37             return splits;
38         }
39     }

第17行使用computeSplitSize(blockSize,minSize,maxsize)计算分片大小。

a.minSize通过以下方式计算:

 long minSize = Math.max(this.getFormatMinSplitSize(), getMinSplitSize(job))

而getFormatMinSplitSize():

protected long getFormatMinSplitSize() {
        return 1L;
    }

而getMinSplitSize(job):

 public static long getMinSplitSize(JobContext job) {
        return job.getConfiguration().getLong("mapred.min.split.size", 1L);
    }

没有设置“mapred.min.split.size”的默认值是0。

所以,不设置“mapred.min.split.size”的话,就使用方法的默认值1代替,而“mapred.min.split.size”的默认值是0,所以minSize的值就是1

b.再看maxSize的计算方式:

   long maxSize = getMaxSplitSize(job);

而getMaxSplitSize():

public static long getMaxSplitSize(JobContext context) {
        return context.getConfiguration().getLong("mapred.max.split.size", 9223372036854775807L);
    }

   没有设置"mapred.max.split.size"的话,就使用方法的默认值 9223372036854775807,而"mapred.max.split.size"并没有默认值,所以maxSize= 9223372036854775807;

c.我们已经能够计算出minSize=1,maxSize= 9223372036854775807,接下来计算分片大小:

 long splitSize = this.computeSplitSize(blockSize, minSize, maxSize);
protected long computeSplitSize(long blockSize, long minSize, long maxSize) {
        return Math.max(minSize, Math.min(maxSize, blockSize));
    }

   显然,分片大小是就是maxSize和blockSize的较小值(minSize=1),那么我们就可以通过设置"mapred.max.split.size"来控制map的数量,只要设置值比物理块小就可以了。使用configuration对象的设置方法如下:

conf.set("mapred.max.split.size",2000000)//单位是字节,物理块是16M

3)可以设置map数量的矩阵乘法代码如下所示:

  1 /**
  2  * Created with IntelliJ IDEA.
  3  * User: hadoop
  4  * Date: 16-3-14
  5  * Time: 下午3:13
  6  * To change this template use File | Settings | File Templates.
  7  */
  8 import org.apache.hadoop.conf.Configuration;
  9 import org.apache.hadoop.fs.FileSystem;
 10 import java.io.IOException;
 11 import java.net.URI;
 12 import org.apache.hadoop.fs.Path;
 13 import org.apache.hadoop.io.*;
 14 import org.apache.hadoop.io.DoubleWritable;
 15 import org.apache.hadoop.io.Writable;
 16 import org.apache.hadoop.mapreduce.InputSplit;
 17 import org.apache.hadoop.mapreduce.Job;
 18 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
 19 import org.apache.hadoop.mapreduce.lib.input.FileSplit;
 20 import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat;
 21 import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
 22 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
 23 import org.apache.hadoop.mapreduce.Reducer;
 24 import org.apache.hadoop.mapreduce.Mapper;
 25 import org.apache.hadoop.filecache.DistributedCache;
 26 import org.apache.hadoop.util.ReflectionUtils;
 27 
 28 public class MutiDoubleInputMatrixProduct {
 29 
 30     public static void initDoubleArrayWritable(int length,DoubleWritable[] doubleArrayWritable){
 31         for (int i=0;i<length;++i){
 32             doubleArrayWritable[i]=new DoubleWritable(0.0);
 33         }
 34     }
 35 
 36     public static  class MyMapper extends Mapper<IntWritable,DoubleArrayWritable,IntWritable,DoubleArrayWritable>{
 37         public DoubleArrayWritable map_value=new DoubleArrayWritable();
 38         public  double[][] leftMatrix=null;/******************************************/
 39         //public Object obValue=null;
 40         public DoubleWritable[] arraySum=null;
 41         public DoubleWritable[] tempColumnArrayDoubleWritable=null;
 42         public DoubleWritable[] tempRowArrayDoubleWritable=null;
 43         public double sum=0;
 44         public double uValue;
 45         public int leftMatrixRowNum;
 46         public int leftMatrixColumnNum;
 47         public void setup(Context context) throws IOException {
 48             Configuration conf=context.getConfiguration();
 49             leftMatrixRowNum=conf.getInt("leftMatrixRowNum",10);
 50             leftMatrixColumnNum=conf.getInt("leftMatrixColumnNum",10);
 51             leftMatrix=new double[leftMatrixRowNum][leftMatrixColumnNum];
 52             uValue=(double)(context.getConfiguration().getFloat("u",1.0f));
 53             tempRowArrayDoubleWritable=new DoubleWritable[leftMatrixColumnNum];
 54             initDoubleArrayWritable(leftMatrixColumnNum,tempRowArrayDoubleWritable);
 55             tempColumnArrayDoubleWritable=new DoubleWritable[leftMatrixRowNum];
 56             initDoubleArrayWritable(leftMatrixRowNum,tempColumnArrayDoubleWritable);
 57             System.out.println("map setup() start!");
 58             //URI[] cacheFiles=DistributedCache.getCacheFiles(context.getConfiguration());
 59             Path[] cacheFiles=DistributedCache.getLocalCacheFiles(conf);
 60             String localCacheFile="file://"+cacheFiles[0].toString();
 61             //URI[] cacheFiles=DistributedCache.getCacheFiles(conf);
 62             //DistributedCache.
 63             System.out.println("local path is:"+cacheFiles[0].toString());
 64             // URI[] cacheFiles=DistributedCache.getCacheFiles(context.getConfiguration());
 65             FileSystem fs =FileSystem.get(URI.create(localCacheFile), conf);
 66             SequenceFile.Reader reader=null;
 67             reader=new SequenceFile.Reader(fs,new Path(localCacheFile),conf);
 68             IntWritable key= (IntWritable)ReflectionUtils.newInstance(reader.getKeyClass(),conf);
 69             DoubleArrayWritable value= (DoubleArrayWritable)ReflectionUtils.newInstance(reader.getValueClass(),conf);
 70             //int valueLength=0;
 71             int rowIndex=0;
 72             int index;
 73             while (reader.next(key,value)){
 74                 index=-1;
 75                 for (Writable val:value.get()){ //ArrayWritable类的get方法返回Writable[]数组
 76                     tempRowArrayDoubleWritable[++index].set(((DoubleWritable)val).get());
 77                 }
 78                 //obValue=value.toArray();
 79                 rowIndex=key.get();
 80                 leftMatrix[rowIndex]=new double[leftMatrixColumnNum];
 81                 //this.leftMatrix=new double[valueLength][Integer.parseInt(context.getConfiguration().get("leftMatrixColumnNum"))];
 82                 for (int i=0;i<leftMatrixColumnNum;++i){
 83                     //leftMatrix[rowIndex][i]=Double.parseDouble(Array.get(obValue, i).toString());
 84                     //leftMatrix[rowIndex][i]=Array.getDouble(obValue, i);
 85                     leftMatrix[rowIndex][i]= tempRowArrayDoubleWritable[i].get();
 86                 }
 87 
 88             }
 89             arraySum=new DoubleWritable[leftMatrix.length];
 90             initDoubleArrayWritable(leftMatrix.length,arraySum);
 91         }
 92         public void map(IntWritable key,DoubleArrayWritable value,Context context) throws IOException, InterruptedException {
 93             //obValue=value.toArray();
 94             InputSplit inputSplit=context.getInputSplit();
 95             String fileName=((FileSplit)inputSplit).getPath().getName();
 96             if (fileName.startsWith("FB")) {
 97                 context.write(key,value);
 98             }
 99             else{
100                 int ii=-1;
101                 for(Writable val:value.get()){
102                     tempColumnArrayDoubleWritable[++ii].set(((DoubleWritable)val).get());
103                 }
104                 //arraySum=new DoubleWritable[this.leftMatrix.length];
105                 for (int i=0;i<this.leftMatrix.length;++i){
106                     sum=0;
107                     for (int j=0;j<this.leftMatrix[0].length;++j){
108                         //sum+= this.leftMatrix[i][j]*Double.parseDouble(Array.get(obValue,j).toString())*(double)(context.getConfiguration().getFloat("u",1f));
109                         //sum+= this.leftMatrix[i][j]*Array.getDouble(obValue,j)*uValue;
110                         sum+= this.leftMatrix[i][j]*tempColumnArrayDoubleWritable[j].get()*uValue;
111                     }
112                     arraySum[i].set(sum);
113                     //arraySum[i].set(sum);
114                 }
115                 map_value.set(arraySum);
116                 context.write(key,map_value);
117             }
118         }
119     }
120     public static class MyReducer extends Reducer<IntWritable,DoubleArrayWritable,IntWritable,DoubleArrayWritable>{
121         public DoubleWritable[] sum=null;
122         // public Object obValue=null;
123         public DoubleArrayWritable valueArrayWritable=new DoubleArrayWritable();
124         public DoubleWritable[] tempColumnArrayDoubleWritable=null;
125         private int leftMatrixRowNum;
126 
127         public void setup(Context context){
128             //leftMatrixColumnNum=context.getConfiguration().getInt("leftMatrixColumnNum",100);
129             leftMatrixRowNum=context.getConfiguration().getInt("leftMatrixRowNum",100);
130             sum=new DoubleWritable[leftMatrixRowNum];
131             initDoubleArrayWritable(leftMatrixRowNum,sum);
132             //tempRowArrayDoubleWritable=new DoubleWritable[leftMatrixColumnNum];
133             tempColumnArrayDoubleWritable=new DoubleWritable[leftMatrixRowNum];
134             initDoubleArrayWritable(leftMatrixRowNum,tempColumnArrayDoubleWritable);
135         }
136         //如果矩阵的计算已经在map中完成了,貌似可以不使用reduce,如果不创建reduce类,MR框架仍然会调用一个默认的reduce,只是这个reduce什么也不做
137         //但是,不使用reduce的话,map直接写文件,有多少个map就会产生多少个结果文件。这里使用reduce是为了将结果矩阵存储在一个文件中。
138         public void reduce(IntWritable key,Iterable<DoubleArrayWritable>value,Context context) throws IOException, InterruptedException {
139             //int valueLength=0;
140             for(DoubleArrayWritable doubleValue:value){
141                 int index=-1;
142                 for (Writable val:doubleValue.get()){
143                     tempColumnArrayDoubleWritable[++index].set(((DoubleWritable)val).get());
144                 }
145                 //valueLength=Array.getLength(obValue);
146                 /*
147                 for (int i=0;i<leftMatrixRowNum;++i){
148                     //sum[i]=new DoubleWritable(Double.parseDouble(Array.get(obValue,i).toString())+sum[i].get());
149                     //sum[i]=new DoubleWritable(Array.getDouble(obValue,i)+sum[i].get());
150                     sum[i].set(tempColumnArrayDoubleWritable[i].get()+sum[i].get());
151                 }
152                 */
153             }
154             //valueArrayWritable.set(sum);
155             valueArrayWritable.set(tempColumnArrayDoubleWritable);
156             context.write(key,valueArrayWritable);
157             /*
158             for (int i=0;i<sum.length;++i){
159                 sum[i].set(0.0);
160             }
161             */
162 
163         }
164     }
165 
166     public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
167         String uri=args[3];
168         String outUri=args[4];
169         String cachePath=args[2];
170         HDFSOperator.deleteDir(outUri);
171         Configuration conf=new Configuration();
172         DistributedCache.addCacheFile(URI.create(cachePath),conf);//添加分布式缓存
173         /**************************************************/
174         //FileSystem fs=FileSystem.get(URI.create(uri),conf);
175         //fs.delete(new Path(outUri),true);
176         /*********************************************************/
177         conf.setInt("leftMatrixColumnNum",Integer.parseInt(args[0]));
178         conf.setInt("leftMatrixRowNum",Integer.parseInt(args[1]));
179         conf.setFloat("u",1.0f);
180         //conf.set("mapred.map.tasks",args[5]);
181         //int mxSplitSize=Integer.valueOf(args[5])
182         conf.set("mapred.max.split.size",args[5]);//hadoop1.2.1中并没有setNumMapTasks方法,只能通过这种方式控制计算分片的大小来控制map数量
183         conf.set("mapred.jar","MutiDoubleInputMatrixProduct.jar");
184         Job job=new Job(conf,"MatrixProdcut");
185         job.setJarByClass(MutiDoubleInputMatrixProduct.class);
186         job.setInputFormatClass(SequenceFileInputFormat.class);
187         job.setOutputFormatClass(SequenceFileOutputFormat.class);
188         job.setMapperClass(MyMapper.class);
189         job.setReducerClass(MyReducer.class);
190         job.setMapOutputKeyClass(IntWritable.class);
191         job.setMapOutputValueClass(DoubleArrayWritable.class);
192         job.setOutputKeyClass(IntWritable.class);
193         job.setOutputValueClass(DoubleArrayWritable.class);
194         FileInputFormat.setInputPaths(job, new Path(uri));
195         FileOutputFormat.setOutputPath(job,new Path(outUri));
196         System.exit(job.waitForCompletion(true)?0:1);
197     }
198 
199 
200 }
201 class DoubleArrayWritable extends ArrayWritable {
202     public DoubleArrayWritable(){
203         super(DoubleWritable.class);
204     }
205 /*
206     public String toString(){
207         StringBuilder sb=new StringBuilder();
208         for (Writable val:get()){
209             DoubleWritable doubleWritable=(DoubleWritable)val;
210             sb.append(doubleWritable.get());
211             sb.append(",");
212         }
213         sb.deleteCharAt(sb.length()-1);
214         return sb.toString();
215     }
216 */
217 }
218 
219 class HDFSOperator{
220     public static boolean deleteDir(String dir)throws IOException{
221         Configuration conf=new Configuration();
222         FileSystem fs =FileSystem.get(conf);
223         boolean result=fs.delete(new Path(dir),true);
224         System.out.println("sOutput delete");
225         fs.close();
226         return result;
227     }
228 }

4)接下来说说如何断点调试hadoop源码,这里以计算文件分片的源码为例来说明。

a.首先找到FileInputFormat类,这个类就在hadoop-core-1.2.1.jar中,我们需要将这个jar包添加到工程中,如下所示:

   虽然这是编译之后的类文件,也就是字节码,但是仍然可以像java源码一样,断点调试,这里我们分别在getSplits()方法和computeSplitSize()方法中添加两个断点,然后使用IDEA在本地直接以Debug方式运行我们的MapReduce程序,结果如下所示:

命中断点,并且我们可以查看相关的变量值。

 

posted @ 2016-04-10 12:38  lz3018  阅读(5103)  评论(0编辑  收藏  举报