MR的wordcount的练习就相当于java的helloworld一样
学习前提:
-
JAVA基础:数据类型、方法、面向对象、反射等等(看懂语法)
-
maven(处理依赖、打包)
-
Hadoop-HDFS的存储原理(看懂集群架构、block等)
-
Hadoop-MapReduce的执行原理(看懂流程)
-
Linux (shell操作)
一共要定义3个类,
- Map类(定义Map阶段怎么处理)
- Reduce类(定义Reduce阶段怎么处理)
- JobMain主类(定义整个MapReduce处理流程,见下)
准备数据
hello,world,hadoop hive,sqoop,flume,hello kitty,tom,jerry,world hadoop
保存成.txt文件
MapReduce 处理数据流程:
Map阶段:
- 输入,读取源数据( setInputFormatClass方法 / 得出K1,V1)
- 设置Mapper类( 继承Mapper类 / K1,V1 转换 K2,V2)
shuffle阶段(直接默认,跳过)
- 分区
- 排序
- 规约
- 分组
Reduce阶段
- 设置Reduce类( 继承Reduce类 / 新K2,V2 转换 K3,V3)
- 输出,保存结果( setOutputFormatClass方法 / 输出K3,V3)
代码:
pom.xml 设置远程仓库、依赖、脚本
<!--指定仓库--> <repositories> <repository> <id>cloudera</id> <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url> </repository> </repositories> <!--打包方式--> <packaging>jar</packaging> <!--包--> <dependencies> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>2.6.0-mr1-cdh5.14.0</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>2.6.0-cdh5.14.0</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-hdfs</artifactId> <version>2.6.0-cdh5.14.0</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-mapreduce-client-core</artifactId> <version>2.6.0-cdh5.14.0</version> </dependency> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>RELEASE</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.4.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <minimizeJar>true</minimizeJar> </configuration> </execution> </executions> </plugin> </plugins> </build>
Mapper类
1.必须继承Mapper类并且指定好K1,V1,K2,V2对应的hadoop数据类型
2.必须重写map方法
import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; /* KEYIN:K1的类型 VALUEIN:V1的类型 KEYOUT:K2的类型 VALUEOUT:V2的类型 注意: Mapper泛型里面要用hadoop自定义的类型(其实就是Hadoop将原本的类型加上序列化操作再封装) 即org.apache.hadoop包下的数据类型 如:long -> LongWritable; String-> Text ... */ public class WordCountMapper extends Mapper<LongWritable, Text, Text, LongWritable> { //目的:将K1,V1 转换 K2,V2 /* 参数: key :K1 行偏移量 value : V1 每一行的文本数据 context:上下文对象,桥梁,连接shuffle阶段 */ @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { //创建对象保存数据 LongWritable ValueOut = new LongWritable(); Text KeyOut = new Text(); //1:拆分文本数据 String[] split = value.toString().split(","); //2:遍历数据,拆分,重组装K2 ,V2 for (String word : split) { KeyOut.set(word); ValueOut.set(1); //3:将K2,V2写入上下文对象当中 context.write(KeyOut, ValueOut); } } } //完毕
Reduce
(同Map类似)
1.必须继承Reducer并指定K2,V2,K3,V3的类型
2.重写reduce方法
import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; //虽然第二个参数即V2是集合,但是仍然使用集合里面的元素类型作为泛型参数 public class WordCountReduce extends Reducer<Text, LongWritable, Text, LongWritable> { //目的:新K2,V2 转成K3,V3,将K3,V3写入上下文 /* 该类方法实现的结果如下: 要处理的数据: 新 K2 V2 Hello <1,1,1> ----------------- 最终输出结果: K3 V3 hello 3 */ @Override protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException { long count = 0; //1:遍历集合,将集合中的数字相加,得到V3 for (LongWritable value : values) { count += value.get(); } //2:将K3和V3写入上下文 context.write(key, new LongWritable(count)); } }
主类JobMain
(依照8个流程步骤编写代码)
1.必须继承Configured类、实现Tool接口
2.必须重写run方法
3.main方法来启动程序
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; //MapReduce需要定义主类来描述job并提交job,用来启动MR-Job //关键点1:必须要继承 Configured 配置类,和实现 Tool 接口,注意是hadoop包下的 public class JobMain extends Configured implements Tool { //关键点2:必须重写一个run方法来调用 @Override public int run(String[] strings) throws Exception { Job job = Job.getInstance(super.getConf(), JobMain.class.getSimpleName()); //打包到集群上面运行时候,必须要添加以下配置,指定程序的main函数 //如果打包出错就需要加上该配置 job.setJarByClass(JobMain.class); //第一步:设置输入类型,读取路径,读取输入文件解析成键值对K1,V1 job.setInputFormatClass(TextInputFormat.class); //集群做好hosts地址映射,不用直接些IP,写node01即可 TextInputFormat.addInputPath(job, new Path("hdfs://node01:8020/wordcount"));//自动读取文件夹所有的文件 //本地运行(必须配置本地hadoop环境) /*TextInputFormat.addInputPath(job, new Path("file:///F:\\mapreduce\\mrinput\\wordcount.txt"));*/ //第二步:设置Mapper类,并设置Map阶段完成之后的输出类型(K2,V2) job.setMapperClass(WordCountMapper.class);//class是反射的知识 job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(LongWritable.class); //第三、四、五、六步,默认,暂时不用写 //第七步:设置Reduce类,并设置Reduce阶段完成之后的输出类型(K3,V3) job.setReducerClass(WordCountReduce.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(LongWritable.class); //第八步:设置输出类型和输出路径 job.setOutputFormatClass(TextOutputFormat.class); Path path = new Path("hdfs://node01:8020/wordcount_out"); //本地运行(必须配置本地hadoop环境) /*TextOutputFormat.setOutputPath(job, new Path("file:///F:\\mapreduce\\mroutput"));*/ TextOutputFormat.setOutputPath(job, path);//如果目录已存在会报错 //改良:避免目录已存在,先判断是否存在,存在就删除 //连接HDFS文件系统 //获取FileSystem FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration()); //判断目录是否存在 boolean hbl = fileSystem.exists(path); if (hbl) { //删除目录 第一个参数是删除哪个目录 第二个参数是否递归删除 fileSystem.delete(path, true); } //等待MR程序完成.. boolean mrb = job.waitForCompletion(true); //返回run主类的执行结果 return mrb ? 0 : 1;//三元运算符 } //关键点3:由于是主启动函数,需要创建main主函数 public static void main(String[] args) throws Exception { Configuration configuration = new Configuration(); //本地执行,可以加入配置参数 //configuration.set("mapreduce.framework.name", "local"); //configuration.set("yarn.resourcemanager.hostname", "local"); Tool tool = new JobMain(); int run = ToolRunner.run(configuration, tool, args); System.exit(run); } }
最后,打包,上次,执行
集群运行:
#hadoop jar 编写好的MR程序Jar包 主方法所在的类 hadoop jar original-mapreducedemo-1.0-SNAPSHOT.jar com.yh.mapreduce.JobMain
本地运行需要的环境:
- 下载解压windows版Hadoop
- 环境变量HADOOP_HOME
- Path:%HADOOP_HOME%\bin
- 复制bin目录下的hadoop.dll到c:\system32目录下
- 重启
End!~