在Hadoop中自定义输入和输出
Hadoop允许用户修改从磁盘加载数据的方式,修改方式主要有两种:配置如何根据HDFS的块生成连续的输入分块;配置记录在map阶段如何出现。为做到这点将用到两个类,即RecordReader和InputFormat。在Hadoop MapReducer框架中,使用这两个类的方式与添加mapper和reducer类似。
hadoop也允许用户通过类似的方式修改数据的存储方式:通过OutputFormat和RecordWriter类。
7.1.1 InputFormat
Hadoop依赖作业的输入格式完成以下三件事情:
- 验证作业的输入配置(即检查数据是否存在)。
- 按照InputSplit类型将输入块和文件拆分成逻辑分块,并将每个数据分块分配给一个map任务处理。
- 创建RecordReader的实现,用它来根据原始的InputSplit生成键/值对。这些键值对将逐个发送供给它们对应的mapper。
InputFormat抽象类包含两个抽象方法:
- getSplits:在实现时通常是通过给定的JobContext对象获取配置的输入并返回一个InputSplits对象的List。输入split有一个方法,能返回与数据在集群中存储位置相关对的机器组成的数组。
- createRecordReader:该方法用于在后端生成一个RecordReader的实现。
7.1.2 RecordReader
RecordReader抽象类的作用是根据给定的InputSplit创建键/值对。InputSplit是面向字节的split视图,而RecorReader能够解析InputSplit并使其可以被mapper处理。模式(schema)在Recorder中定义,它仅仅依赖于record reader的实现,它的改变是基于作业希望获得什么样的输入。RecordReader从输入源中读取字节并将其转换成WriteableComparable的键以及Writable的值。当创建自定义输入格式时,自定义数据类型十分常见,这是将信息呈现给mapper的一种很好的面向对象的方式。
RecordReader使用创建输入split时产生的边界内的数据生成键/值对。在基于文件的输入的上下文中,读取数据“开始”的位置中RecordReader能够开始生成键/值对的地方。而读取数据“结束”的位置是文件中RecordReader能够开始生成键/值对的地方。而读取数据“结束”的位置是文件中RecordReader应该停止读取记录的地方。就API而言,它并不关心是否有硬边界。
RecordReader抽象类有一些方法必须重载:
initialize 该方法将map任务分配的InputSplit和TaskAttemptContext作为输入参数,然后初始化record reader。对于基于文件的输入格式,很适合在这里寻找文件开始读取的字节位置。
getCurrentKey和getCurrentValue 框架使用这两个方法将生成的键/值对传递给mapper的实现。如果可能的话,一定要重用这些方法返回的对象。
nextKeyValue 与InputFormat类中相应的方法一样,该方法读取了一个键/值对后返回trur,直到所有数据都消耗完。
getProgress 与inputFormat类中相对应的方法一样,这是一个可选的方法。
close 框架调用该方法完成相关的清理工作。
7.1.3 outputFormat
和输入格式类似,Hadoop依赖作业的输出格式完成以下两个主要任务: 1.验证作业的输出配置 2.创建RecordWriter的实现,它负责写作业的输出。
OutputFormat抽象类包含以下三个抽象方法需要实现:
- checkOutputSpecs 这个方法用于验证作业指定的输出,如在作业提交
- getReocrdWriter 这个方法返回RecordReader的实现,该实现负责将键/值对序列化到一个输出。
- getOutputCommiter 在初始化期间,作业的输出提交者设置每个任务,成功完成时提交任务,并且当每个任务结束后执行清理。
7.1.4 RecordWriter
RecordWriter抽象类负责将键值对写入到文件系统或其他输出中。RecordWriter抽象类是一个更为简单的接口,仅仅包含以下两个方法:
- write 在写每个键/值对时,框架将调用该方法。
- close 当没有键/值对需要输出后,框架将调用该方法。