MapReduce和yarn集群
背景:我们在对HDFS上的文件进行操作的时候,之前我们需要通过先将原来的文件进行下载,在本地上进行编码运行和调试,再改文件并将其传入到hdfs中,但是这样的操作给我们带来了很多的不便。以下介绍通过框架MapReduce和yarn集群对HDFS上的数据进行操作。
MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map(映射)“和"Reduce(归约)”,是它们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。 当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归约)函数,用来保证所有映射的键值对中的每一个共享相同的键组。 (摘自百度百科)
MapReduce主要分为两个阶段:
- map阶段——程序MapTask
- reduce阶段——程序ReduceTsak
yarn集群的工作调度机制
实验内容,对hdfs中的的一个txt文本进行英文单词的统计。
第一种方法
这里的第一种方法是将我们的程序完全的放在windows下运行,不过在运行的时候我们要将文件通过yarn集群传递到hdfs(虚拟机)里面就会出很多的问题,例如:上传时的用户名设置、文件路径设置、jar包的设置等等。
程序逻辑图
第一步:导包
导入我们需要的jar包,这里我们就是用maven来导入我们需要的jar包
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.8.1</version>
</dependency>
第二步:编写Mapper类
这个类是将我们在HDFS中获取到的文件一行一行的读入,并将格式进行切割数据,其按照key,value值进行分类,并将分类好的数据保存到contxt中,传递给Reducer。
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;
/*
* 在Mapper<>中
* 第一个参数:是map task读取到的数据的key的类型,是一行的起始偏移量Long
* 第二个参数:是map task读取到的数据的value的类型,是一行的内容String
* 第三个参数:用户的自定义map方法要返回的结果kv数据的key的类型,在wordcount逻辑中,我们需要返回的是单词String
* 第四个参数:用户的自定义map方法要返回的结果kv数据的value的类型,在wordcount逻辑中,我们需要返回的是单词整数Integer
*
* 但是在MapReduce中,map产生的数据需要传输给reduce,需要进行序列化和反序列化,而jdk中的原生序列化机制产生的数据量比较冗余,就会导致数据在MapReduce在运行过程中传输效率低
* 所以hadoop专门设计了自己的序列化机制,那么,MapReduce中传输的数据类型就必须实现hadoop自己的序列化接口
*
* hadoop为jdk中的常用基本数据类型Long String Integer Float等数据类型封装了自己的实现了hadoop序列化的接口类型:
* Long—>LongWritable
* String->Text
* Integer->IntWritable
* Float->FloatWritable
* */
public class WordcountMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// 切单词
String line = value.toString();
String[] words = line.split(" ");
for (String word : words) {
// context.write(word,1);这样写会报错,因为context的两个参数的类型是上面我们封装的Text和Inwritable的类型,所以在输入String和int类型时会有问题
context.write(new Text(word),new IntWritable(1));
}
}
}
但是我们这里要注意,在这里面的字符串类型都要写成我们在hadoop给我们封装的字符串类型,
hadoop为jdk中的常用基本数据类型Long String Integer Float等数据类型封装了自己的实现了hadoop序列化的接口类型:
- Long—>LongWritable
- String->Text
- Integer->IntWritable
- Float->FloatWritable
第三步:编写Reducer类
这个类就是对Mapper这个类传过来的(Key,Value)值进行数据上的操作,并将其返回给任务区写回到虚拟机中。
import java.io.IOException;
import java.util.Iterator;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
/*
* 第一个参数:对应的应该是我们在WordcountMapper里面传给我们的第三个参数
* 第二个参数:对应的应该是我们在WordcountMapper里面传个我们的第四个参数
* 第三个参数:对应的应该是我们的单词,String类型,所以应该是Text
* 第四个参数:对应的应该是我们的总数,int类型,所以应该是InWritable
* */
public class WordcountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
@Override
protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {
int count = 0;
Iterator<IntWritable> iterator = values.iterator();
while(iterator.hasNext()){
IntWritable value = iterator.next();
count += value.get();
}
context.write(key, new IntWritable(count));
}
}
第四步:在虚拟机中yarn的程序
yarn集群中有两个角色:
主节点:Resource Manager 1台
从节点:Node Manager N台
Resource Manager一般安装在一台专门的机器上
Node Manager应该与HDFS中的data node重叠在一起
修改namenode虚拟机中yarn-site.xml
<property>
<name>yarn.resourcemanager.hostname</name>
<value>hdp-04</value> <!--写自己虚拟机的名称-->
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.resource.memory-mb</name>
<value>2048</value> <!--给每一个任务的内存量-->
</property>
<property>
<name>yarn.nodemanager.resource.cpu-vcores</name>
<value>2</value>
</property>
然后复制到每一台机器上
然后在第一台虚拟机上,修改hadoop的slaves文件,列入要启动nodemanager的机器
然后将到所有机器的免密登陆配置好
然后,就可以用脚本启动yarn集群:
sbin/start-yarn.sh
停止:
sbin/stop-yarn.sh
第五步:编写主函数类
主函数类的主要的目的:
- 封装本次job运行时所需要的必要函数
- 跟yarn进行怀虎,将MapReduce程序成功的启动,运行
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
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.net.URI;
public class JobSubmitter {
public static void main(String[] args) throws Exception {
// 在代码中设置JVM系统参数,用于给job对象来获取访问HDFS的用户身份
System.setProperty("HADOOP_USER_NAME","kjxy");
Configuration conf=new Configuration();
// 1.设置job运行时要访问的默认文件系统
conf.set("fs.defaultFS","hdfs://192.168.200.137:9000");
// 2.设置job提交到哪里去运行
conf.set("mapreduce.framework.name","yarn");
conf.set("yarn.resourcemanager.hostname","192.168.200.137");
// 3、如果从windows系统上运行这个job提交客户端程序,则需要加这个跨平台提交的参数
conf.set("mapreduce.app-submission.cross-platform","true");
Job job = Job.getInstance(conf);
// 1、封装参数:jar包所在的位置,获取到本类的路径
job.setJar("d:/wc.jar");
// job.setJarByClass(JobSubmitter.class);
// 2、封装参数:本次job所要调用的Mapper实现类、Reducer实现类
job.setMapperClass(WordcountMapper.class);
job.setReducerClass(WordcountReducer.class);
// 3、封装参数:本次job的Mapper实现类产生的结果数据的key,value类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
Path output=new Path("/wordcount/output");
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.200.137:9000"),conf,"kjxy");
if(fs.exists(output)){
fs.delete(output,true);
}
// 4、封装参数:本次job要处理的输入数据集所在路径
FileInputFormat.setInputPaths(job,new Path("/wordcount/input/"));
// 5、最终结果的输出路径
FileOutputFormat.setOutputPath(job,output);//注意输出路径必须不存在,如果已存在会抛异常的
// 6、封装参数:想要启动的reduce task的数量
job.setNumReduceTasks(2);
// 7、提交job给yarn
boolean res = job.waitForCompletion(true);
System.exit(res?0:1);
}
}