Hadoop生态
HDFS
HDFS写数据流程
(1)客户端通过 Distributed FileSystem 模块向 NameNode 请求上传文件,NameNode 检查目标文件是否已存在,父目录是否存在
(2)NameNode 返回是否可以上传,若不能,返回异常
(3)确定可以上传,客户端请求第一个 block 上传到哪几个 datanode服务器上
(4)NameNode 返回3个 datanode 节点,假定分别为dn1、dn2、dn3
(5)客户端通过 FSDataOutputStream 模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成
(6)dn1、dn2、dn3逐级应答客户端
(7)客户端开始往dn1上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以 packet(64KB) 为单位,dn1收到一个packet 就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答
(8)当一个block传输完成之后,客户端再次请求 NameNode 上传第二个block的服务器(重复执行3-7步)
HDFS读数据流程
(1)首先调用FileSystem.open()方法,获取到 DistributedFileSystem 实例
(2)DistributedFileSystem 向 Namenode 发起 PRC(远程过程调用)请求获得文件的开始部分或全部 block 列表,对于每个返回的块,都包含块所在的 DataNode 地址。这些 DataNode 会按照 Hadoop 定义的集群拓扑结构得出客户端的距离,然后再进行排序。如果客户端本身就是一个DataNode,那么他将从本地读取文件
(3) DistributedFileSystem 会向客户端 client 返回一个支持文件定位的输入流对象 FSDataInputStream,用于客户端读取数据。FSDataInputStream 包含一个 DFSInputStream 对象,这个对象用来管理 DataNode 和 NameNode 之间的 I/O
(4)客户端调用read()方法,DFSInputStream 就会找出离客户端最近的datanode并连接datanode
(5)DFSInputStream 对象中包含文件开始部分的数据块所在的DataNode地址,首先它会连接包含文件第一个块最近的DataNode。随后,在数据流中重复调用read()函数,直到这个块全部读完为止。如果第一个block块的数据读完,就会关闭指向第一个block块的datanode连接,接着读取下一个block块
(6)如果第一批block都读完了,DFSInputStream 就回去NameNode拿下一批blocks的location,然后继续读,如果所有的block块都读完,这时就会关闭掉所有的流
MapReduce
(1)map task进程开始,利用客户端指定的inputformat来读取数据
(2)利用RecordReader,调用read()方法,一次读一行,每一行就是一份文件片
(3)行偏移量为k,每行的内容为v,生成键值对(k,v)
(4)获得的键值对(k,v)经过Mapper的map方法逻辑处理形成新的键值对map(k,v),此时k是单词,v是个数
通过context.write()方法把map(k,v)输出到OutputCollector收集器;
(5)OutputCollector把键值对map(k,v)写入到环形缓冲区中,此时shuffle开始。环形缓冲区默认大小为100M,环形缓冲区里面的数据到80%时,就会溢出
(6)溢出前需要对环形缓冲区里的键hash出一个partition值,根据partition分区,相同分区内根据键进行排序
(7)此时如果写了Combiner方法,会把同一个key的键值对合并在一起,生成有分区且相同分区内有序的大文件。这个合并的目的是为了减少网络传输。
(8)如果没有写Combiner方法,环形缓冲区开始溢出到map task本地磁盘,此时是有分区且分区内有序的小文件,如果数据量过大,会溢出多个文件;
(9)小文件会被merge归并排序成有分区且分区内有序的大文件,此时map task结束
(10)reduce任务开始,不同的reducetask根据分区号,去不同的maptask中拷贝与自己相同分区号的文件到reduce task本地磁盘
(11)各个reduce task把拷贝过来的与自己同一分区号的文件进行merge归并排序成一个大文件,文件内部的键值对根据键排序,此时shuffle过程结束
(12)GroupingComparator(k,nextk)对大文件里面的数据根据k进行分组,从文件中每次取出一组键值对(k,values),
经过Reducer的reduce方法逻辑处理形成新的键值对reduce(k,v),此时k是单词,v是这个单词的个数
(13)调用context.write(k,v)方法写入到OutPutFormat里,然后将数据结果落盘
WordMapper类
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class WordMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
private Text word = new Text();
private IntWritable count = new IntWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String[] words = value.toString().trim()
.replaceAll(",|\\.|!|\\?|;|\"", "").split(" ");
for (String word : words) {
this.word.set(word);
context.write(this.word,count);
}
}
}
WordReducer类
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
private IntWritable count = new IntWritable();
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable value : values) {
sum += value.get();
}
count.set(sum);
context.write(key,count);
}
}
WordPartitioner类
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
public class WordPatritioner extends Partitioner<Text, IntWritable> {
@Override
public int getPartition(Text text, IntWritable intWritable, int numberReduceTasks) {
return text.toString().length()%numberReduceTasks;
}
}
启动类
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
public class WordTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//创建hdfs访问路径配置信息
Configuration config = new Configuration();
config.set("fs.defaultFS","hdfs://192.168.247.130:9000");
//创建mapreduce计算任务
Job job = Job.getInstance(config,"wordCount");
//设置主类,定位外部jar资源
job.setJarByClass(WordTest.class);
//设置任务数:和文件分片数和所存储的DN数有关
job.setNumReduceTasks(2);
//设置分区器Partitioner
job.setPartitionerClass(WordPatritioner.class);
//设置Combiner
job.setCombinerClass(WordReducer.class);
//设置Mapper
job.setMapperClass(WordMapper.class);
//设置Reducer
job.setReducerClass(WordReducer.class);
//设置Mapper的输出键值类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//设置Reducer的输出键值类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//设置hdfs输入文件绑定job
FileInputFormat.setInputPaths(job,new Path("/kb10/Pollyanna.txt"));
FileOutputFormat.setOutputPath(job,new Path("/kb10/wc01"));
System.out.println(job.waitForCompletion(true));
}
}
将测试文章放入hdfs中
[root@single ~]# hdfs dfs -put Pollyanna.txt /kb10/
Java中运行启动类WordTest
2020-11-22 21:06:51,283 INFO [org.apache.hadoop.conf.Configuration.deprecation] - session.id is deprecated. Instead, use dfs.metrics.session-id
2020-11-22 21:06:51,284 INFO [org.apache.hadoop.metrics.jvm.JvmMetrics] - Initializing JVM Metrics with processName=JobTracker, sessionId=
2020-11-22 21:07:00,797 WARN [org.apache.hadoop.mapreduce.JobSubmitter] - Submitting tokens for job: job_local188587393_0001
[org.apache.hadoop.mapred.LocalJobRunner] - reduce task executor complete.
...
...
...
2020-11-22 21:07:02,037 INFO [org.apache.hadoop.mapreduce.Job] - Job job_local188587393_0001 running in uber mode : false
2020-11-22 21:07:02,038 INFO [org.apache.hadoop.mapreduce.Job] - map 100% reduce 100%
2020-11-22 21:07:02,038 INFO [org.apache.hadoop.mapreduce.Job] - Job job_local188587393_0001 completed successfully
2020-11-22 21:07:02,053 INFO [org.apache.hadoop.mapreduce.Job] - Counters: 35
File System Counters
FILE: Number of bytes read=37065
FILE: Number of bytes written=1359678
FILE: Number of read operations=0
FILE: Number of large read operations=0
FILE: Number of write operations=0
HDFS: Number of bytes read=30364
HDFS: Number of bytes written=9124
HDFS: Number of read operations=38
HDFS: Number of large read operations=0
HDFS: Number of write operations=16
Map-Reduce Framework
Map input records=35
Map output records=1365
Map output bytes=12733
Map output materialized bytes=6687
Input split bytes=111
Combine input records=1365
Combine output records=528
Reduce input groups=528
Reduce shuffle bytes=6687
Reduce input records=528
Reduce output records=528
Spilled Records=1056
Shuffled Maps =3
Failed Shuffles=0
Merged Map outputs=3
GC time elapsed (ms)=0
Total committed heap usage (bytes)=1025507328
Shuffle Errors
BAD_ID=0
CONNECTION=0
IO_ERROR=0
WRONG_LENGTH=0
WRONG_MAP=0
WRONG_REDUCE=0
File Input Format Counters
Bytes Read=7591
File Output Format Counters
Bytes Written=4580
true
查看hdfs中的数据结果
因为指定了3个job,所以生成了三个文件
查看wordcount结果
[root@single ~]# hdfs dfs -cat /kb10/wc01/part-r-*