MapReduce环境搭建及编程
前置环境
需要搭建好hadoop伪分布式集群平台,未搭建好的可以看这个教程 万字详解,Hadoop大数据技术简介及 伪分布式集群搭建快速入门教程
Eclipse环境配置
Eclipse(Windows 本地系统的)
1. 安装插件:
hadoop-eclipse-plugin-2.7.3.jar
地址:https://pan.baidu.com/s/1BaAOQkZaY4RvUPuBPVlgJg 提取码:067u
将插件复制到eclipse目录下的dropins目录
2. 配置MapReduce环境:
配置好Hadoop插件后,接下来配置开发环境,连接到Hadoop集群,相关步骤如下:
(1) 增加Map/Reduce功能区
(2) 增加Hadoop集群的连接
点蓝色小象新建一个Locations
这里需要填的有:
相关的连接配置信息有:
①Location name:命名新建的Hadoop连接,随便写
②Map/Reduce(V2)Master:填写Hadoop集群的ResourceManager的IP和端口,从以前配置Hadoop集群的yarn-site.xml找
③DFS Master:填写Hadoop集群的NameNode的IP和端口,从以前配置的core-site.xml找
如果没有配置相关端口,那么就用默认的设置即可,最后finish配置完成。
这里有一个坑,点击已创建的Locations可能会报错,如果设置没问题的话,不用管他
(3) 浏览HDFS上的目录及文件
配置完成Hadoop集群的连接信息后,就可以在Eclipse界面浏览HDFS上的目录和文件了,还可以通过鼠标来进行文件操作
需要注意的是,执行操作后,需要刷新HDFS列表,从而获得文件目录的最新状态。
3. 新建MapReduce工程
本部分将在Eclipse建立MapReduce工程MemberCount,大体按照下面四个步骤进行:
(1)导入MapReduce运行依赖的jar包
首先将虚拟环境中的hadoop包导入到本地环境下,导入过程如下:
这里需要在本地windows系统下解压一个hadoop,并将路径设置上去。
(2)创建MapReduce工程
(3)在MapReduce Project的界面创建工程
(4)查验是否创建好工程
设置 log4j.properties
需要在工程的src目录下新建一个log4j.properties文件,其内容如下
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
其他杂七杂八的坑点及环境设置
1. 计算机用户名不能出现空格
cmd中使用下述命令查看用户名
whoami
修改方法如下:
右键开始按钮,选择【计算机管理】,如下图所示进行修改,修改后需重启后生效,但是不急着重启,因为后边还有。
2. hadoop.dll 缺失
在这个链接中https://github.com/4ttty/winutils找到相应版本的hadoop.dll下载
然后将其放到C:\Windows\System32目录下
3. Windows下还需要winutils.exe文件
https://github.com/4ttty/winutils找到相应版本的winutils.exe下载
然后将其放到本地hadoop目录下的bin文件夹里,然后进行环境变量的设置
新建如下系统变量HADOOP_HOME,值为hadoop的安装目录
然后设置Path变量,添加一项内容为
%HADOOP_HOME%\bin
重启后生效
4. 修改dfs.permissions为false
切换到下图路径下
使用命令vim hdfs-site.xml
新增下面的property,若已存在将其value修改为false
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>
MapReduce程序案例——词频统计程序
目录结构
代码
WordCountMapper类,继承于Hadoop的Mapper:
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 WordCountMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// key 偏移量 value 每行文本:I love Guiyang
// 按照空格对每行文本进行分词操作:[I,love,Guiyang]
String line = value.toString(); // 数据类型的转换
String[] words = line.split(" "); // 对字符串进行拆分 [I,love,Guiyang]
for (int i = 0; i < words.length; i++) {
// (I,1)
Text word = new Text(words[i]);
IntWritable value2 = new IntWritable(1);
context.write(word,value2);
}
}
}
WordCountReducer类,继承于Hadoop的Reducer:
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordCountReducer extends Reducer<Text, IntWritable,Text, IntWritable> {
@Override
protected void reduce(Text k3, Iterable<IntWritable> v3, Context context) throws IOException, InterruptedException {
// 进行求和操作,需要计算v3的长度
// <I,[1,1]>
int count = 0;
for (IntWritable v: v3) {
int value = v.get();
count += value;
}
context.write(k3,new IntWritable(count));
}
}
WordCountJob类:
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 WordCountJob {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { // 实列化Job
Job job = Job.getInstance();
job.setJarByClass(WordCountJob.class); // 设置Mapper
job.setMapperClass(WordCountMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class); // 设置Reducer
job.setReducerClass(WordCountReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class); // 指明输入文件的路径
FileInputFormat.setInputPaths(job,new Path("hdfs://192.168.80.128:8020/input/One Hundred Years of Solitude.txt")); // 指明输出文件的路径
FileOutputFormat.setOutputPath(job,new Path("d:/mr_result/wc01")); // 开始运行任务
boolean completion = job.waitForCompletion(true);
if (completion){
System.out.println("程序运行成功~");
}
else { System.out.println("程序运行失败~"); }
}
}
在本地执行
创建输入文件wordcount.txt,其内容为:
I love Guiyang I love Guizhou Guiyang is the capical of Guizhou
在WordCountJob类中的第16行设置好本地输入文件路径
FileInputFormat.setInputPaths(job,new Path("d:/wordcount.txt"));
在WordCountJob类中右键,选择【Run As】-【1 Java Application】
若报错如下图所示,说明输出路径已存在,删掉就可以了
成功运行则如下图所示
结果如下:
在Hadoop伪分布式集群上执行
需先启动相应服务,命令为:
start-all.sh
在DFS Locations相应路径上右键,点击【Upload files to DFS】
将测试文件One Hundred Years of Solitude.txt上传至HDFS文件服务器上
需要完成的任务是统计词频并找出词频最高的前100个词及其出现次数
分为两步:统计词频和按词频排序
统计词频
与之前的程序相差不大,仅在WordCountMapper类中增加了对符号的处理,新的WordCountMapper类代码如下:
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 WordCountMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// key 偏移量 value 每行文本:I love Guiyang
// 按照空格对每行文本进行分词操作:[I,love,Guiyang]
String line = value.toString(); // 数据类型的转换
line = line.replaceAll("--------------------------------------------------------------------------------", "");
line = line.replaceAll("[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~0-9]", "");//去除符号和数字
String[] words = line.split(" "); // 对字符串进行拆分 [I,love,Guiyang]
for (int i = 0; i < words.length; i++) {
// (I,1)
Text word = new Text(words[i]);
IntWritable value2 = new IntWritable(1);
context.write(word,value2);
}
}
}
在WordCountJob类中的第16行设置好云输入文件路径
FileInputFormat.setInputPaths(job,new Path("hdfs://192.168.80.128:8020/input/One Hundred Years of Solitude.txt")); // 指明输出文件的路径
在WordCountJob类中右键,选择【Run As】-【2 Run on Hadoop】
成功运行则如下图所示
结果如下:
按词频排序
将词频统计结果保存到res1文件中并上传至HDFS服务器中作为排序的输入文件。
新建一个按词频排序程序的工程,结构如下:
按词频排序代码如下:
tongji类,自定义的数据类型:
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class tongji implements WritableComparable <tongji>{
private long number;
public tongji(long number) {
this.number = number;
}
public tongji() {
}
public long getNumber() {
return number;
}
public void setNumber(long number) {
this.number = number;
}
@Override
public String toString() {
return String.valueOf(number);
}
@Override
public int compareTo(tongji o) {
int result;
// 按照总流量大小,倒序排列
if (number > o.getNumber()) {
result = -1;
}else if (number < o.getNumber()) {
result = 1;
}else {
result = 0;
}
return result;
}
@Override
public void write(DataOutput out) throws IOException {
out.writeLong(number);
}
@Override
public void readFields(DataInput in) throws IOException {
number =in.readLong();
}
}
WordCountMapper类,继承于Hadoop的Mapper:
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class WordcountMapper extends Mapper<LongWritable, Text, tongji, Text>{
tongji k = new tongji();
Text v = new Text();
@Override
protected void map(LongWritable key, Text value,Context context)
throws IOException, InterruptedException {
//获取一行
String line = value.toString();
//切割单词
String[] fields = line.split("\t");
// 3 封装对象
String danci = fields[0];
long number = Long.parseLong(fields[1]);
k.setNumber(number);
v.set(danci);
context.write(k,v);
}
}
WordCountReducer类,继承于Hadoop的Reducer:
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordCountReducer extends Reducer<tongji,Text,Text, tongji> {
@Override
protected void reduce(tongji key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
for (Text value : values) {
context.write(value,key);
}
}
}
WordcountDriver类:
import java.io.IOException;
import org.apache.hadoop.fs.Path;
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;
public class WordcountDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//获取job
Job job = Job.getInstance();
//设置jar包
job.setJarByClass(WordcountDriver.class);
//关联mapper和reducer
job.setMapperClass(WordcountMapper.class);
job.setReducerClass(WordCountReducer.class);
//map输出的k和v
job.setMapOutputKeyClass(tongji.class);
job.setMapOutputValueClass(Text.class);
//最终输出kv
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(tongji.class);
// 指明输入文件的路径
FileInputFormat.setInputPaths(job,new Path("hdfs://192.168.80.128:8020/input/res1.txt"));
// 指明输出文件的路径
FileOutputFormat.setOutputPath(job,new Path("hdfs://192.168.80.128:8020/output/mr_result/wc02"));
//提交job
// 开始运行任务
boolean completion = job.waitForCompletion(true);
if (completion){
System.out.println("程序运行成功~");
}
else { System.out.println("程序运行失败~"); }
}
}
执行结果如下:
查看 集群所有应用程序 的信息
在:8088中可以看到程序在hadoop上运行的记录