MR 筆記五

1.InputFormat
InputFormat常見子類包括:
TextInputFormat (普通文本文件,
MR框架默認的讀取實現類型)
KeyValueTextInputFormat(讀取一行文本數據按照指定分隔符,把數據封裝爲
kv類型)
NLineInputF ormat(讀取數據按照行數進行劃分分片
)
CombineTextInputFormat(合併小文件,避免啓動過多MapTask任務)
自定義InputFormat

2.CombineTextInputFormat
// 如果不設置InputFormat,它默認用的是TextInputFormat.class
job.setInputFormatClass(CombineTextInputFormat.class);
//虛擬存儲切片最大值設置4m
CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);

CombineTextInputFormat切片原理
切片生成過程分爲兩部分:虛擬存儲過程和切片過程
假設設置setMaxInputSplitSize值爲4M
四個小文件:1.txt -->2M ;2.txt-->7M;3.txt-->0.3M;4.txt--->8.2M
虛擬存儲過程:把輸入目錄下所有文件大小,依次和設置的setMaxInputSplitSize值進行比 較,如果不大於設置的最大值,邏輯上劃分一個。如果輸入文件大於設置的最大值且大於 兩倍,那麼以最大值切割一塊;當剩餘數據大小超過設置的最大值且不大於最大值2倍,此時 將文件均分成2個虛擬存儲塊(防止出現太小切片)。
比如如setMaxInputSplitSize值爲4M,輸入文件大小爲8.02M,則先邏輯上分出一個4M的 塊。剩餘的大小爲4.02M,如果按照4M邏輯劃分,就會出現0.02M的非常小的虛擬存儲文 件,所以將剩餘的4.02M文件切分成(2.01M2.01M)兩個文件。
1.txt-->2M;2M<4M;一個塊;
2.txt-->7M;7M>4M,但是不大於兩倍,均勻分成兩塊;兩塊:每塊3.5M
3.txt-->0.3M;0.3<4M ,0.3M<4M ,一個塊
4.txt-->8.2M;大於最大值且大於兩倍;一個4M的塊,剩餘4.2M分成兩塊,每塊2.1M
所有塊信息: 2M,3.5M3.5M0.3M4M2.1M2.1M 7個虛擬存儲塊。
切片過程
判斷虛擬存儲的文件大小是否大於setMaxInputSplitSize值,大於等於則單獨形成一個 切片。
如果不大於則跟下一個虛擬存儲文件進行合併,共同形成一個切片。
按照之前輸入文件:有4個小文件大小分別爲2M7M0.3M以及8.2M這四個小文件, 則虛擬存儲之後形成7個文件塊,大小分別爲: 2M,3.5M3.5M0.3M4M2.1M2.1M
最終會形成3個切片,大小分別爲: (2+3.5M,(3.5+0.3+4M,(2.1+2.1M
 
 
3.自定義InputFormat
1.extends FileInputFormat
2.
自定義RecordReader
3.在driver端指定自定義的InputFormat
代碼:
public class CustomFileInputformat extends FileInputFormat<Text, BytesWritable> {
    /**
     * 文件不可切分
     */
    @Override
    protected boolean isSplitable(JobContext context, Path filename) {
        return false;
    }
    /**
     * createRecordReader  讀取文本的對象
     *
     * @param inputSplit
     * @param taskAttemptContext
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public RecordReader<Text, BytesWritable> createRecordReader(InputSplit inputSplit, TaskAttemptContext taskAttemptContext) throws IOException, InterruptedException {
        CustomRecordReader recordReader = new CustomRecordReader();
        recordReader.initialize(inputSplit, taskAttemptContext);
        return recordReader;
    }
}
public class CustomRecordReader extends RecordReader<Text, BytesWritable> {

    private Configuration configuration;
    /**
     * 切片
     */
    private FileSplit split;


    /**
     * 輸出的kv
     */
    private Text k = new Text();
    private BytesWritable value = new BytesWritable();

    /**
     * 是否讀取到內容的標識符
     */
    private boolean flag = true;


    /**
     * 初始化方法  把切片已經上下文提升爲全局
     *
     * @param inputSplit
     * @param taskAttemptContext
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public void initialize(InputSplit inputSplit, TaskAttemptContext taskAttemptContext) throws IOException, InterruptedException {
        //獲取到文件切片以及配置文件對象
        this.split = (FileSplit) inputSplit;
        configuration = taskAttemptContext.getConfiguration();
    }

    /**
     * 用來讀取數據的方法
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public boolean nextKeyValue() throws IOException, InterruptedException {
        if (flag) {
            // 1 定義緩存區 存放讀取的數據
            byte[] contents = new byte[(int) split.getLength()];
            FileSystem fs = null;
            FSDataInputStream fis = null;
            // 2 獲取文件系統
            Path path = split.getPath();
            fs = path.getFileSystem(configuration);
            // 3 讀取數據
            fis = fs.open(path);
            // 4 讀取文件內容
            IOUtils.readFully(fis, contents, 0, contents.length);
            // 5 輸出文件內容
            value.set(contents, 0, contents.length);
            // 6 獲取文件路徑及名稱
            String name = split.getPath().toString();
            // 7 設置輸出的key值
            k.set(name);
            IOUtils.closeStream(fis);
            flag = false;
            return true;
        }
        return false;
    }

    /**
     * 獲取key
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public Text getCurrentKey() throws IOException, InterruptedException {
        return k;
    }

    /**
     * 獲取value
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public BytesWritable getCurrentValue() throws IOException, InterruptedException {
        return value;
    }

    /**
     * 獲取進度
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    public float getProgress() throws IOException, InterruptedException {
        return 0;
    }

    /**
     * 關閉
     *
     * @throws IOException
     */
    @Override
    public void close() throws IOException {

    }

4.自定義OutputFormat

1.extends FileOutputFormat
2.自定義RecordWriter
3.在driver端指定自定義的OutputFormat
代碼:
public class CustomOutputFormat extends FileOutputFormat<Text, NullWritable> {
    @Override
    public RecordWriter<Text, NullWritable> getRecordWriter(TaskAttemptContext taskAttemptContext) throws IOException, InterruptedException {
        //獲取文件系統對象
        FileSystem fs = FileSystem.get(taskAttemptContext.getConfiguration());
        //指定輸出數據的文件
        Path lagouPath = new Path("e:/lagou.log");
        Path otherLog = new Path("e:/other.log");
        //獲取輸出流
        final FSDataOutputStream lagouOut = fs.create(lagouPath);
        final FSDataOutputStream otherOut = fs.create(otherLog);
        return new CustomWriter(lagouOut, otherOut);
    }
}
public class CustomWriter extends RecordWriter<Text, NullWritable> {

    private FSDataOutputStream lagouOut;
    private FSDataOutputStream otherOut;

    public CustomWriter(FSDataOutputStream lagouOut, FSDataOutputStream otherOut) {
        this.lagouOut = lagouOut;
        this.otherOut = otherOut;
    }

    @Override
    public void write(Text key, NullWritable nullWritable) throws IOException, InterruptedException {
        // 判斷是否包含“lagou”輸出到不同文件
        if (key.toString().contains("lagou")) {
            lagouOut.write(key.toString().getBytes());
            lagouOut.write("\r\n".getBytes());
        } else {
            otherOut.write(key.toString().getBytes());
            otherOut.write("\r\n".getBytes());
        }
    }

    @Override
    public void close(TaskAttemptContext taskAttemptContext) throws IOException, InterruptedException {
        IOUtils.closeStream(lagouOut);
        IOUtils.closeStream(otherOut);
    }
}

 

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