maven:
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.10.1</version>
</dependency>
代码:
WordCountMapper类代码
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/**
* @ProjectName: HadoopTest
* @Package: PACKAGE_NAME
* @ClassName: WordCountMapper
* @Author: 82042
* @Description:
* @Date: 2020/11/15 21:48
* @Version: 1.0
*/
/*
* 4个泛型
* KEYIN:k1的类型,这里是偏移量
* VALUEIN:v1的类型,文本数据
* KEYOUT:k2的类型 每个单词
* VALUEOUT:v2的类型 固定的数字
* MapReduce它定义了自定义的类型,就是为了方便序列化,只是把Long和String进行了封装
* */
public class WordCountMapper extends Mapper<LongWritable, Text,Text,LongWritable> {
//继承map方法:将k1 v1转成k2 v2
/*
* 参数
* key:k1
* value:v1 每一行的文本数据
* context:上下文对象,把各个流程串接到一起
* */
/*
* 如何将k1 v1转为k2 v2?
* 研究样例
* k1 v1
* 0 hello,world,hadoop
* 15 hdfs,hive,hello
* ---------------------
* k2 v2
* hello 1
* world 1
* hdfs 1
* hadoop 1
* hello 1
* */
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//这两个对象用来放字符串,和int
Text text = new Text();
LongWritable longWritable = new LongWritable();
//1、将一行的文本数据进行拆分
//由于text不能调用split,要用toString转换成字符串,因为测试数据用,分割
String[] split = value.toString().split(",");
//2、遍历数组,组装k2 v2
for (String word : split) {
//要转换成对应的类型才能写进去
//context.write(new Text(word),new LongWritable(1));这行代码效率不太好,向上提取
text.set(word);
longWritable.set(1);
context.write(text,longWritable);
}
//3、将k2和v2写入上下文
}//map结束
}
Reducer代码
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* @ProjectName: HadoopTest
* @Package: PACKAGE_NAME
* @ClassName: WordCountReducer
* @Author: 82042
* @Description:
* @Date: 2020/11/15 22:12
* @Version: 1.0
*/
/*
* 4个泛型
* KEYIN:k2类型
* VALUEIN:v2类型
* KEYOUT:k3类型
* VALUEOUT:v3类型
*
* */
public class WordCountReducer extends Reducer<Text, LongWritable,Text,LongWritable> {
//将新的k2、v2转成k3、v3
/*
* 参数:
* key:新k2
* values:集合 新v2
* context:上下文对象
* ------------------
* 如何将的k2,v2转化为k3,v3
* k2 v2
* hello <1,1,1>
* world <1,1>
* hadoop <1>
* ----------------
* k3 v3
* hello 3
* world 2
* hadoop 1
* 发现只要遍历将每个单词集合中的1相加即可
* */
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
long count=0;
//1、遍历集合,将集合中的数字相加,得到k3
for (LongWritable value : values) {
count +=value.get();//get方法返回long
}
//2、将k3和v3写入上下文中
context.write(key,new LongWritable(count));
}
}
JobMain代码
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.net.URI;
/**
* @ProjectName: HadoopTest
* @Package: PACKAGE_NAME
* @ClassName: JobMain
* @Author: 82042
* @Description:
* @Date: 2020/11/16 16:52
* @Version: 1.0
*/
//主类要继承Configured并且实现Tool
public class JobMain extends Configured implements Tool {
//该方法用于指定一个job任务
@Override
public int run(String[] strings) throws Exception {
//1、创建一个job任务对象,super.getConf获取从main方法中放入的Configuration对象
Job job = Job.getInstance(super.getConf(), "wordCount");
//如果打包运行出错,则需要加该配置
job.setJarByClass(JobMain.class);
//2、配置job任务对象,8个步骤
//第一步:指定文件的读取方式和读取路径
job.setInputFormatClass(TextInputFormat.class);
//hdfs目录
TextInputFormat.addInputPath(job,new Path("hdfs://node01:8020/wordcount"));
//本地目录
// TextInputFormat.addInputPath(job, new Path("file:///D:\\Mapreduce\\input.txt"));
//第二步:map阶段的处理方式
job.setMapperClass(WordCountMapper.class);
//设置map阶段k2的类型
job.setMapOutputKeyClass(Text.class);
//设置v2的类型
job.setMapOutputValueClass(LongWritable.class);
//第三、四、五、六采用默认的方式
//第七步:指定Reduce的处理方式
job.setReducerClass(WordCountReducer.class);
//设置k3
job.setOutputKeyClass(Text.class);
//设置v3
job.setOutputValueClass(LongWritable.class);
//第八步:设置输出类型
job.setOutputFormatClass(TextOutputFormat.class);
//设置一个输出的路径,这里不存在的路径会自动创建,这个目录不能已经存在
//提取出Path,便于之后output已经存在就删除的代码
Path path = new Path("hdfs://node01:8020/wordcount_out");
//hdfs目录
TextOutputFormat.setOutputPath(job,path);
//本地目录
// TextOutputFormat.setOutputPath(job,new Path("file:///D:\\MapReduce\\output"));
//判断output存在与否
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
boolean exists = fileSystem.exists(path);
//存在则删除
if (exists)fileSystem.delete(path,true);//true是递归删除
//等待任务结束,返回的是任务的成功还是失败
boolean b = job.waitForCompletion(true);
//这个返回值最终给了main方法中调用run的地方
return b? 0:1;
}
//入口方法
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration();
//启动job任务,其中的Tool参数可以直接newJobMain,这个run方法就是上面的run方法
//返回值是任务是否成功,0成功否则失败
//这个传进去的Configuration是放到Configured中,但JobMain继承了所以用super.getConf可以得到
int run = ToolRunner.run(configuration, new JobMain(), args);
System.exit(run);
}
}
代码测试
hadoop jar mapReduceCode-1.0-SNAPSHOT.jar(jar包名) JobMain()
二、本地运行模式
将数据的输入和输出都放在本地,就能够以单进程的形式运行了
改一下输入和输出就行
//hdfs目录
// TextInputFormat.addInputPath(job,new Path("hdfs://node01:8020/wordcount"));
//本地目录
TextInputFormat.addInputPath(job, new Path("file:///D:\\Mapreduce\\input.txt"));
。
。
。
//设置一个输出的路径,这里不存在的路径会自动创建,这个目录不能已经存在
//hdfs目录
// TextOutputFormat.setOutputPath(job,new Path("hdfs://node01:8020/wordcount_out"));
//本地目录
TextOutputFormat.setOutputPath(job,new Path("file:///D:\\MapReduce\\output"));