Mapreduce 学习日记
一、搭建环境
重要的事情先说三遍!!!
先搭好环境!!!
先搭好环境!!!
先搭好环境!!!
我是在Ubuntu系统下安装好Hadoop,并通过eclipse来编译运行MapReduce。
本教程主要测试环境:
Ubuntu 14.04
Hadoop 2.6.0(伪分布式)
Eclipse 3.8
1.1安装Ubuntu
我使用的是 Ubuntu 14.04 64位 作为系统环境(Ubuntu 12.04,Ubuntu16.04 也行,32位、64位均可),请自行安装系统(可参考使用VirtualBox安装Ubuntu)。
安装SSH、配置SSH无密码登陆
Ubuntu 默认已安装了 SSH client,此外还需要安装 SSH server:
sudo apt-get install openssh-server
安装后,可以使用如下命令登陆本机:
ssh localhost
此时会有如下提示(SSH首次登陆提示),输入 yes 。然后按提示输入密码 hadoop,这样就登陆到本机了。
但这样登陆是需要每次输入密码的,我们需要配置成SSH无密码登陆比较方便。
首先退出刚才的 ssh,就回到了我们原先的终端窗口,然后利用 ssh-keygen 生成密钥,并将密钥加入到授权中:
exit # 退出刚才的 ssh localhost
cd ~/.ssh/ # 若没有该目录,请先执行一次ssh localhost
ssh-keygen -t rsa # 会有提示,都按回车就可以
cat ./id_rsa.pub >> ./authorized_keys # 加入授权
此时再用 ssh localhost 命令,无需输入密码就可以直接登陆了,如下图所示
1.2安装Java环境
安装Java环境的方式有多种,我就不一一介绍了。
首先,我是手动安装Java环境
这是我下载JDK1.8的安装包jdk-8u162-linux-x64.tar.gz的地址,
可以点击这里到百度云盘下载JDK1.8安装包(提取码:99bg)。然后把压缩格式的文件jdk-8u162-linux-x64.tar.gz下载到本地电脑,保存在“/下载”目录下。
cd /usr/lib
sudo mkdir jvm #创建/usr/lib/jvm目录用来存放JDK文件
cd ~ #进入hadoop用户的主目录
cd 下载 #注意区分大小写字母,刚才已经下载的JDK安装 包jdk-8u162-linux-x64.tar.gz上传到该目录下
sudo tar -zxvf ./jdk-8u162-linux-x64.tar.gz -C /usr/lib/jvm #把JDK文件解压到/usr/lib/jvm目录下
设置环境变量:
编译~/.bashrc,在里面加入下面内容
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_162
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
保存.bashrc文件并退出编辑器。然后,继续执行如下命令让.bashrc文件的配置立即生效:
source ~/.bashrc
然后查看JAVA环境配置是否有效
java -version
如果能够在屏幕上返回如下信息,则说明安装成功
1.3安装Hadoop
Hadoop可以通过 http://mirror.bit.edu.cn/apache/hadoop/common/ 或者
http://mirrors.cnnic.cn/apache/hadoop/common/ 下载
我将 Hadoop 安装至 /usr/local/ 中:
sudo tar -zxf ~/下载/hadoop-2.6.0.tar.gz -C /usr/local # 解压到/usr/local中
cd /usr/local/
sudo mv ./hadoop-2.6.0/ ./hadoop # 将文件夹名改为hadoop
sudo chown -R hadoop ./hadoop # 修改文件权限
Hadoop 解压后即可使用。输入如下命令来检查 Hadoop 是否可用,成功则会显示 Hadoop 版本信息:
cd /usr/local/hadoop
./bin/hadoop version
Hadoop伪分布式配置
Hadoop 的配置文件位于 /usr/local/hadoop/etc/hadoop/ 中,伪分布式需要修改2个配置文件 core-site.xml 和 hdfs-site.xml 。Hadoop的配置文件是 xml 格式,每个配置以声明 property 的 name 和 value 的方式来实现。
修改配置文件 core-site.xml (通过 gedit 编辑会比较方便: gedit ./etc/hadoop/core-site.xml),将当中的
修改为下面配置:
<configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>file:/usr/local/hadoop/tmp</value>
<description>Abase for other temporary directories.</description>
</property>
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
</configuration>
同样的,修改配置文件 hdfs-site.xml:
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/usr/local/hadoop/tmp/dfs/name</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/usr/local/hadoop/tmp/dfs/data</value>
</property>
</configuration>
配置完成后,执行 NameNode 的格式化:
cd /usr/local/hadoop
./bin/hdfs namenode -format
接着开启 NameNode 和 DataNode 守护进程。
cd /usr/local/hadoop
./sbin/start-dfs.sh #start-dfs.sh是个完整的可执行文件,中间没有空格
若出现如下SSH提示,输入yes即可
启动完成后,可以通过命令 jps 来判断是否成功启动,若成功启动则会列出如下进程: “NameNode”、”DataNode” 和 “SecondaryNameNode”(如果 SecondaryNameNode 没有启动,请运行 sbin/stop-dfs.sh 关闭进程,然后再次尝试启动尝试)。如果没有 NameNode 或 DataNode ,那就是配置不成功,请仔细检查之前步骤,或通过查看启动日志排查原因。
成功启动后,可以访问 Web 界面 http://localhost:50070/ 查看 NameNode 和 Datanode 信息,还可以在线查看 HDFS 中的文件。
二、介绍MapReduce 体系结构
MapReduce体系结构主要由四个部分组成,分别是:Client、JobTracker、TaskTracker以及Task。
1)Client
用户通过client提交程序到JobTracker端
并且我们可以通过Client提供的一些接口查看作业运行状态
2)JobTracker
JobTracker负责资源监控和作业调度
JobTracker 监控所有TaskTracker与Job的健康状况,一旦发现失败,就将相应的任务转移到其他节点
JobTracker 会跟踪任务的执行进度、资源使用量等信息,并将这些信息告诉任务调度器(TaskScheduler),而调度器会在资源出现空闲时,选择合适的任务去使用这些资源
3)TaskTracker
TaskTracker 会周期性地通过“心跳”将本节点上资源的使用情况和任务的运行进度汇报给JobTracker,同时接收JobTracker 发送过来的命令并执行相应的操作(如启动新任务、杀死任务等)
TaskTracker 使用“slot”等量划分本节点上的资源量(CPU、内存等)。一个Task 获取到一个slot 后才有机会运行,而Hadoop调度器的作用就是将各个TaskTracker上的空闲slot分配给Task使用。slot 分为Map slot 和Reduce slot 两种,分别供MapTask 和Reduce Task 使用
4)Task
Task 分为Map Task 和Reduce Task 两种,均由TaskTracker 启动
三、介绍MapReduce 基本的执行流程
一个基于MapReduce的WordCount程序主要由一下几个部分组成:Split、Map、Shuffle/Combine/sort、Reduce
1、Split
将程序的输入数据进行切分,每一个 split 交给一个 Map Task 执行。split的数量可以自己定义。
2、Map
输入为一个split中的数据,对split中的数据进行拆分,并以 < key, value> 对的格式保存数据,其中 key 的值为一个单词,value的值固定为 1。如 < I , 1>、< wish, 1> …
3、Shuffle/Combine/sort
这几个过程在一些简单的MapReduce程序中并不需要我们关注,因为源代码中已经给出了一些默认的Shuffle/Combine/sort处理器,这几个过程的作用分别是:
Combine:对Map Task产生的结果在本地节点上进行合并、统计等,以减少后续整个集群间的Shuffle过程所需要传输的数据量。
Shuffle / Sort:将集群中各个Map Task的处理结果在集群间进行传输,排序,数据经过这个阶段之后就作为 Reduce 端的输入。
4、Reduce
Reduce Task的输入数据其实已经不仅仅是简单的< key, value>对,而是经过排序之后的一系列key值相同的< key, value>对。Reduce Task对其进行统计等处理,产生最终的输出。
四、源码
package org.apache.hadoop.examples;
import java.io.IOException;
import java.util.Iterator;
import java.util.StringTokenizer;
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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
public class WordCount {
public WordCount() {
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
String[] otherArgs=new String[]{"input","output"}; /* 直接设置输入参数和输出*/
if(otherArgs.length < 2) {
System.err.println("Usage: wordcount <in> [<in>...] <out>");
System.exit(2);
}
// 实例化job,传入参数
Job job = Job.getInstance(conf, "word count"); /* getInstance 在主函数开始时调用,返回一个实例化对象,此对象是static的,在内存中保留着它的引用,即内存中有一块区域专门用来存放静态方法和变量,*/
job.setJarByClass(WordCount.class); //使用反射机制,加载程序
job.setMapperClass(TokenizerMapper.class); //设置job的map阶段的执行类
job.setCombinerClass(IntSumReducer.class); //设置job的combine阶段的执行类
job.setReducerClass(IntSumReducer.class); //设置job的reduce阶段的执行类
job.setOutputKeyClass(Text.class); //设置程序的输出的key值的类型
job.setOutputValueClass(IntWritable.class); //设置程序的输出的value值的类型
for(int i = 0; i < otherArgs.length - 1; ++i) {
FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
}//获取我们给定的参数中,输入文件所在路径
FileOutputFormat.setOutputPath(job, new Path(otherArgs[otherArgs.length - 1])); //获取我们给定的参数中,输出文件所在路径
System.exit(job.waitForCompletion(true)?0:1);//等待任务完成,任务完成之后退出程序
}
public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> { //Reduce() 阶段
private IntWritable result = new IntWritable();
public IntSumReducer() {
}
public void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
int sum = 0;
IntWritable val;
for(Iterator i$ = values.iterator(); i$.hasNext(); sum += val.get()) {
val = (IntWritable)i$.next();
}
this.result.set(sum);
context.write(key, this.result);
}
}
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable> { //Map() 阶段阶段
private static final IntWritable one = new IntWritable(1);
private Text word = new Text();
public TokenizerMapper() {
}
public void map(Object key, Text value, Mapper<Object, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
while(itr.hasMoreTokens()) {
this.word.set(itr.nextToken());
context.write(this.word, one);
}
}
}
}
关于源码部分,如果有不清楚的可以参考MapReduce WordCount 源码详细解析
五、结果
input文件夹就是我输入文件的路径,output文件夹则是输出的结果,都是保存在HDFS上面的,可以自行上传或下载。
这是我mapreduce执行的结果,太长了就只截了一点。
总结
通过本次使用eclipse编译运行Mapreduce,使我更加清楚的认识了MapReduce的体系结构和基本执行流程。其体系结构主要包括:Client、JobTracker、TaskTracker以及Task。Client就是用户,用户将程序提交给JobTracker,而JobTracker转手发给Task Scheduler,由Task Scheduler来完成任务分工,再将任务分工发给JobTracker,JobTracker通过“心跳”将任务发送给TaskTracker,由它来执行操作。MapTask负责将数据变成<key,value>的形式,然后将完成的数据发送给JobTracker,JobTracker将分配新的任务给Map Task并将它完成的数据交给Reduce Task统计,TaskTracker 会周期性地通过“心跳”将本节点上资源的使用情况和任务的运行进度汇报给JobTracker。
本次实验是参照[厦门大学数据库实验室完成的](http://dblab.xmu.edu.cn/blog/285/)