Mapreduce原始碼分析分片、處理流程
InputFormat(程式碼以TextInputFormat為例):
FileinputFormat中有三個重要方法:
1).isSplitable
2).getSplits
3).createRecordReader
一.isSplitable方法:
改方法返回值為bool型別,判斷是否進行分片。
二.getSplits方法:
該方法返回值為List
InputSplit是一個抽象類,FileSplit繼承該類,FileSplit有以下屬性:
private Path file; //檔案路徑
private long start; //該塊儲存的檔案的起始下標
private long length; //該塊儲存塊大小
private String[] hosts; //儲存該塊的主機
getSplits方法原始碼:
-
/**
-
* Generate the list of files and make them into FileSplits.
-
* @param job the job context
-
* @throws IOException
-
*/
-
public List<InputSplit> getSplits(JobContext job) throws IOException {
-
long minSize = Math.max(getFormatMinSplitSize(), getMinSplitSize(job));
-
long maxSize = getMaxSplitSize(job);
-
-
// generate splits
-
List<InputSplit> splits = new ArrayList<InputSplit>();
-
List<FileStatus> files = listStatus(job); //該方法會遍歷輸入目錄和目錄的子目錄,將檔案資訊儲存到List
中。
-
for (FileStatus file: files) { //遍歷該List將檔案放入到 splits 中。
-
Path path = file.getPath();
-
long length = file.getLen();
-
if (length != 0) {
-
BlockLocation[] blkLocations;
-
if (file instanceof LocatedFileStatus) {
-
blkLocations = ((LocatedFileStatus) file).getBlockLocations();
-
} else {
-
FileSystem fs = path.getFileSystem(job.getConfiguration());
-
blkLocations = fs.getFileBlockLocations(file, 0, length);
-
}
-
if (isSplitable(job, path)) { //如果為分片
-
long blockSize = file.getBlockSize();
-
long splitSize = computeSplitSize(blockSize, minSize, maxSize); //獲取分片大小
-
-
long bytesRemaining = length;
-
while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) { // SPLIT_SLOP值為1.1 如果 檔案總大小/分片大小<1.1 即使該檔案大小大於塊大小,那該檔案也不會分割。
-
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
- splits.add(makeSplit(path, length-bytesRemaining, splitSize,blkLocations[blkIndex].getHosts()));
-
// makeSplit方法: protected FileSplit makeSplit(Path file, long start, long length, String[] hosts) {
-
// return new FileSplit(file, start, length, hosts);
-
- bytesRemaining -= splitSize; // bytesRemaining 減去 已經分片的大小splitSize
-
}
-
-
if (bytesRemaining != 0) {
-
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
-
splits.add(makeSplit(path, length-bytesRemaining, bytesRemaining,
-
blkLocations[blkIndex].getHosts()));
-
}
-
} else { // not splitable //如果不分片,則直接將檔案放入到List中,起始位置為0,大小為檔案總大小。
-
splits.add(makeSplit(path, 0, length, blkLocations[0].getHosts()));
-
}
-
} else {
-
//Create empty hosts array for zero length files
-
splits.add(makeSplit(path, 0, length, new String[0]));
-
}
-
}
-
// Save the number of input files for metrics/loadgen
-
job.getConfiguration().setLong(NUM_INPUT_FILES, files.size());
-
LOG.debug("Total # of splits: " + splits.size());
return splits;
}
該方法返回一個RecordReader物件。以支援分片的LineRecordReader為例:
該物件有四個重要方法:
1).initialize
2).nextKeyValue
3).getCurrentKey
4).getCurrentValue
1.initialize方法:
-
public void initialize(InputSplit genericSplit,
-
TaskAttemptContext context) throws IOException {
-
FileSplit split = (FileSplit) genericSplit;
-
Configuration job = context.getConfiguration();
-
this.maxLineLength = job.getInt(MAX_LINE_LENGTH, Integer.MAX_VALUE);
-
start = split.getStart(); //獲取起始下標
-
end = start + split.getLength(); //獲取大小
-
final Path file = split.getPath();
-
-
// open the file and seek to the start of the split
-
final FileSystem fs = file.getFileSystem(job);
-
fileIn = fs.open(file);
-
-
CompressionCodec codec = new CompressionCodecFactory(job).getCodec(file); //根據檔案字尾名獲取相應解碼器。 如果輸入檔案為壓縮檔案則會自動獲取。
-
if (null!=codec) { //如果是壓縮檔案
-
isCompressedInput = true;
-
decompressor = CodecPool.getDecompressor(codec);
-
if (codec instanceof SplittableCompressionCodec) {
-
final SplitCompressionInputStream cIn =
-
((SplittableCompressionCodec)codec).createInputStream(
-
fileIn, decompressor, start, end,
-
SplittableCompressionCodec.READ_MODE.BYBLOCK);
-
if (null == this.recordDelimiterBytes){ //recordDelimiterBytes為終止符,如果讀取一行時遇到該符號則結束。
-
in = new LineReader(cIn, job); //獲取到LineReader LineReader封裝了一個InputStream
-
} else {
-
in = new LineReader(cIn, job, this.recordDelimiterBytes);
-
}
-
-
start = cIn.getAdjustedStart();
-
end = cIn.getAdjustedEnd();
-
filePosition = cIn;
-
} else {
-
if (null == this.recordDelimiterBytes) {
-
in = new LineReader(codec.createInputStream(fileIn, decompressor),
-
job);
-
} else {
-
in = new LineReader(codec.createInputStream(fileIn,
-
decompressor), job, this.recordDelimiterBytes);
-
}
-
filePosition = fileIn;
-
}
-
} else {
-
fileIn.seek(start);
-
if (null == this.recordDelimiterBytes){
-
in = new LineReader(fileIn, job);
-
} else {
-
in = new LineReader(fileIn, job, this.recordDelimiterBytes);
-
}
-
-
filePosition = fileIn;
-
}
-
// If this is not the first split, we always throw away first record
-
// because we always (except the last split) read one extra line in
-
// next() method.
-
if (start != 0) {
-
start += in.readLine(new Text(), 0, maxBytesToConsume(start));
-
}
-
this.pos = start;
- }
-
public boolean nextKeyValue() throws IOException {
-
if (key == null) {
-
key = new LongWritable();
-
}
-
key.set(pos); //pos預設值為FileSplit的start,也就是行號。
-
if (value == null) {
-
value = new Text();
-
}
-
int newSize = 0;
-
// We always read one extra line, which lies outside the upper
-
// split limit i.e. (end - 1)
-
while (getFilePosition() <= end) {
-
newSize = in.readLine(value, maxLineLength, //獲取到value
-
Math.max(maxBytesToConsume(pos), maxLineLength));
-
pos += newSize;
-
if (newSize < maxLineLength) {
-
break;
-
}
-
-
// line too long. try again
-
LOG.info("Skipped line of size " + newSize + " at pos " +
(pos - newSize));
}
if (newSize == 0) {
key = null;
value = null;
return false;
} else {
return true;
}
}
看一下map的run方法:
-
public void run(Context context) throws IOException, InterruptedException {
-
setup(context);
-
try {
-
while (context.nextKeyValue()) {
-
map(context.getCurrentKey(), context.getCurrentValue(), context);
-
}
-
} finally {
-
cleanup(context);
-
}
- }
可能不是很全面,主要目的為個人備忘
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29754888/viewspace-1249907/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- mapreduce job提交流程原始碼級分析(三)原始碼
- mapreduce job提交流程原始碼級分析(二)(原創)原始碼
- 原始碼解析Java Attach處理流程原始碼Java
- Scrapy原始碼閱讀分析_4_請求處理流程原始碼
- spring事務管理原始碼分析(二)事務處理流程分析Spring原始碼
- MapReduce 詳解與原始碼分析原始碼
- 【MyBatis原始碼分析】insert方法、update方法、delete方法處理流程(上篇)MyBatis原始碼delete
- 【MyBatis原始碼分析】insert方法、update方法、delete方法處理流程(下篇)MyBatis原始碼delete
- Mapreduce Job提交流程原始碼和切片原始碼詳解原始碼
- MyBatis原始碼分析之核心處理層MyBatis原始碼
- Laravel Excpetions(錯誤處理) 原始碼分析Laravel原始碼
- Sidekiq 訊號處理原始碼分析IDE原始碼
- MapReduce流程分析(R1)
- Hadoop2原始碼分析-MapReduce篇Hadoop原始碼
- EGADS框架處理流程分析框架
- #21 Go errors 處理及 zap 原始碼分析GoError原始碼
- MapReduce——客戶端提交任務原始碼分析客戶端原始碼
- MapReduce —— MapTask階段原始碼分析(Input環節)APT原始碼
- MapReduce —— MapTask階段原始碼分析(Output環節)APT原始碼
- MapReduce框架Mapper和Reducer類原始碼分析框架APP原始碼
- WindowManager呼叫流程原始碼分析原始碼
- 執行流程原始碼分析原始碼
- external-attacher原始碼分析(2)-核心處理邏輯分析原始碼
- kube-scheduler原始碼分析(2)-核心處理邏輯分析原始碼
- ViewGroup事件分發和處理原始碼分析View事件原始碼
- ThinkPHP6 原始碼分析之請求處理PHP原始碼
- 原始碼分析:Android訊息處理機制原始碼Android
- 【Zookeeper】原始碼分析之請求處理鏈(一)原始碼
- arm 中斷配置以及處理的原始碼分析原始碼
- DML對QUERY CACHE 處理過程之原始碼分析原始碼
- MapReduce job在JobTracker初始化原始碼級分析原始碼
- Flutter啟動流程原始碼分析Flutter原始碼
- View繪製流程原始碼分析View原始碼
- 原始碼分析Retrofit請求流程原始碼
- Mybatis執行流程原始碼分析MyBatis原始碼
- apiserver原始碼分析——啟動流程APIServer原始碼
- Activity啟動流程原始碼分析原始碼
- Spark job分配流程原始碼分析Spark原始碼