我的例子是运行3台虚拟机

master:192.168.27.100
slave1:192.168.27.101
slave2:192.168.27.102

一、代码和文件传入hadoop中

代码运行需要foodmart.txt文件,和代码一起打包如下链接中:

链接:说明文档和代码 提取码:o1re

代码如下:

//package com.ghgj.mazh.mapreduce.wc.demo1;
 
import java.io.IOException;
 
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.LongWritable;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.HashPartitioner;
import org.apache.hadoop.util.GenericOptionsParser;
 

public class WordCountMR {
 
	/**
	 * 该main方法是该mapreduce程序运行的入口,其中用一个Job类对象来管理程序运行时所需要的很多参数:
	 * 比如,指定用哪个组件作为数据读取器、数据结果输出器 指定用哪个类作为map阶段的业务逻辑类,哪个类作为reduce阶段的业务逻辑类
	 * 指定wordcount job程序的jar包所在路径 .... 以及其他各种需要的参数
	 */
	public static void main(String[] args) throws Exception {
		// 指定hdfs相关的参数
		//Configuration conf = new Configuration();
		
		// 手动设置,该MapReduce程序读取的数据来自于HDFS集群
		//conf.set("fs.defaultFS", "hdfs://master:9000");
		// 设置运行程序的用户是hadoop用户,就是安装hadoop集群的用户。如果该程序在Hadoop集群中使用hadoop用户进行运行,则可以去掉
		//System.setProperty("HADOOP_USER_NAME", "hadoop");
		
		/**
		 * 以上的配置信息,事实上,在实际企业生产环境中,也可以使用conf.addResource方法进行加载。
		 * 当然如果配置文件的名字是core/hdfs/yarn/mapred-site/default.xml的话。 那么会自动加载的。
		 */
//		conf.addResource("hadoop_config/core-site.xml");
//		conf.addResource("hadoop_config/hdfs-site.xml");
 
		// 如果想让MR程序运行在特定的YARN集群之上,则可以使用一下代码,然后,这两信息,在安装集群的配置文件中都有配置的
		// conf.set("mapreduce.framework.name", "yarn");
		// conf.set("yarn.resourcemanager.hostname", "hadoop04");

		Configuration conf = new Configuration();
		String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
		if(otherArgs.length != 2) {
			System.err.println("Usage: wordcount <in> <out>");
			System.exit(2);
		}
		// 通过Configuration对象获取Job对象,该job对象会组织所有的该MapReduce程序所有的各种组件
		Job job = Job.getInstance(conf);

		job.setJar("WordCountMR.jar");
		// 设置jar包所在路径
		job.setJarByClass(WordCountMR.class);
 
		// 指定mapper类和reducer类
		job.setMapperClass(WordCountMapper.class);
		job.setReducerClass(WordCountReducer.class);
 
		/**
		 * 指定maptask的输出类型
		 * Mapper的输入key-value类型,由MapReduce框架决定, 默认情况下就是 LongWritable和Text类型
		 * 
		 * 假如 mapTask的输出key-value类型,跟reduceTask的输出key-value类型一致,那么,以上两句代码可以不用设置
		 */
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);
 
		/**
		 * 指定reducetask的输出类型
		 * 如果reduceTask的输入key-value类型就是 mapTask的输出key-value类型。可以不需要指定
		 */
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);
 
		// 为job指定输入数据的组件和输出数据的组件,以下两个参数是默认的,所以不指定也是OK的
		job.setInputFormatClass(TextInputFormat.class);
		job.setOutputFormatClass(TextOutputFormat.class);
 
		// 为该mapreduce程序制定默认的数据分区组件。默认是 HashPartitioner.class
		job.setPartitionerClass(HashPartitioner.class);
 
		/**
		 * 指定该mapreduce程序数据的输入和输出路径:
		 * inputPath目录可以是文件,也可以是目录
		 * outputPath路径必须是不存在的目录
		 */
		Path inputPath = new Path("/input2/foodmart.txt");
		Path outputPath = new Path("/output2");
 
		// 设置该MapReduce程序的ReduceTask的个数
		// job.setNumReduceTasks(3);
 
		// 该段代码是用来判断输出路径存在不存在,存在就删除,虽然方便操作,但请谨慎
		//FileSystem fs = FileSystem.get(conf);
		//if (fs.exists(outputPath)) {
		//	fs.delete(outputPath, true);
		//}
 
		// 设置wordcount程序的输入路径
		FileInputFormat.setInputPaths(job, inputPath);
		// 设置wordcount程序的输出路径
		FileOutputFormat.setOutputPath(job, outputPath);
 
		// job.submit();
		// 最后提交任务(verbose布尔值 决定要不要将运行进度信息输出给用户)
		boolean waitForCompletion = job.waitForCompletion(true);
		// 主线程程序根据MapReduce程序的运行结果成功与否退出。
		System.exit(waitForCompletion ? 0 : 1);
	}
 
	/**
	 * Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
	 * 
	 * KEYIN 是指框架读取到的数据的key的类型,在默认的InputFormat下,读到的key是一行文本的起始偏移量,所以key的类型是Long
	 * VALUEIN 是指框架读取到的数据的value的类型,在默认的InputFormat下,读到的value是一行文本的内容,所以value的类型是String
	 * KEYOUT 是指用户自定义逻辑方法返回的数据中key的类型,由用户业务逻辑决定,在此wordcount程序中,我们输出的key是单词,所以是String
	 * VALUEOUT 是指用户自定义逻辑方法返回的数据中value的类型,由用户业务逻辑决定,在此wordcount程序中,我们输出的value是单词的数量,所以是Integer
	 * 
	 * 但是,String ,Long等jdk中自带的数据类型,在序列化时,效率比较低,hadoop为了提高序列化效率,自定义了一套序列化框架
	 * 所以,在hadoop的程序中,如果该数据需要进行序列化(写磁盘,或者网络传输),就一定要用实现了hadoop序列化框架的数据类型
	 * 
	 * Long ----> LongWritable 
	 * String ----> Text 
	 * Integer ----> IntWritable 
	 * Null ----> NullWritable
	 */
	static class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
 
		/**
		 * LongWritable key : 该key就是value该行文本的在文件当中的起始偏移量
		 * Text value : 就是MapReduce框架默认的数据读取组件TextInputFormat读取文件当中的一行文本
		 */
		@Override
		protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
 
			// 切分单词
			String[] words = value.toString().split(":");
			String[] items = words[0].toString().split(" ");
			String[] utilities = words[2].toString().split(" ");


			// for (String item : items) {
			// 	// 每个单词计数一次,也就是把单词组织成<hello,1>这样的key-value对往外写出
			// 	context.write(new Text(item), new IntWritable(1));
			// }

			for (int i = 0; i < items.length; i++){
				context.write(new Text(items[i]), new IntWritable(Integer.parseInt(utilities[i])));
			}
		}
	}
 
	/**
	 * 首先,和前面一样,Reducer类也有输入和输出,输入就是Map阶段的处理结果,输出就是Reduce最后的输出
	 * reducetask在调我们写的reduce方法,reducetask应该收到了前一阶段(map阶段)中所有maptask输出的数据中的一部分
	 * (数据的key.hashcode%reducetask数==本reductask号),所以reducetaks的输入类型必须和maptask的输出类型一样
	 * 
	 * reducetask将这些收到kv数据拿来处理时,是这样调用我们的reduce方法的: 先将自己收到的所有的kv对按照k分组(根据k是否相同)
	 * 将某一组kv中的第一个kv中的k传给reduce方法的key变量,把这一组kv中所有的v用一个迭代器传给reduce方法的变量values
	 */
	static class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
 
		/**
		 * Text key : mapTask输出的key值
		 * Iterable<IntWritable> values : key对应的value的集合(该key只是相同的一个key)
		 * 
		 * reduce方法接收key值相同的一组key-value进行汇总计算
		 */
		@Override
		protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
 
			// 结果汇总
			int sum = 0;
			for (IntWritable v : values) {
				sum += v.get();
			}
			// 汇总的结果往外输出
			context.write(key, new IntWritable(sum));
		}
	}
}

启动好完全分布式hadoop后,根据代码的输入路径在hsdoop中创建输入文件夹input2,在master节点上输入hadoop fs -mkdir /input2创建input2文件夹,然后输入hadoop fs -ls /查看是否创建成功,如下图所示:

hadoop实例化出现问题 hadoop应用实例代码_mapreduce


然后输入hadoop fs -moveFromLocal /software/codefile/foodmart.txt /input2将本地文件foodmart.txt移动到hadoop服务器上。你的下载好的本地文件存在路径就写哪里,比如,我的路径是/software/codefile/foodmart.txt

移动好之后输入hadoop fs -ls /input2查看是否移动成功,如下图所示:

hadoop实例化出现问题 hadoop应用实例代码_java_02

注意: 你从上面链接下载好的foodmart.txt一般都存在你的本地电脑中,你需要将它移到虚拟机中,可以选择用FileZilla软件移,关于FileZilla的下载,你可以自己去搜教程下载,也可以点击链接下载:
种子文件(因为是hadoop配置所需要的全部文件,所以比较大,不止有FileZilla软件)
提取码:co3u

关于如何下载种子文件,点击链接查看教程:下载种子文件教程

二、编译代码

编译用到的jar包:(路径是你配置时候hadoop存放的路径,我这里是/software/hadoop)
/software/hadoop/share/hadoop/common/hadoop-common-3.3.0.jar
/software/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-client-core-3.3.0.jar
/software/hadoop/share/hadoop/common/lib/commons-cli-1.2.jar

根据自己代码的存放路径进入该目录,比我我存放的路径是/software/codefile/src,我就输入cd /software/codefile/src进入。

hadoop实例化出现问题 hadoop应用实例代码_java_03

然后在终端输入:

#编译java代码
javac -classpath /opt/software/hadoop/share/hadoop/common/hadoop-common-3.3.0.jar:/opt/software/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-client-core-3.3.0.jar:/opt/software/hadoop/share/hadoop/common/lib/commons-cli-1.2.jar -d classes/ *.java
#将编译好的代码打包为jar包
jar -cvf WordCountMR.jar classes

会自动生成classes文件夹,并且该文件夹下面有编译好的.class文件,并且在/software/codefile/src目录下会生成一个WordCountMR.jar文件,如下图所示:

hadoop实例化出现问题 hadoop应用实例代码_java_04


hadoop实例化出现问题 hadoop应用实例代码_hadoop_05

三、运行代码

在终端输入:

#执行java代码命令
hadoop jar /software/codefile/src/WordCountMR.jar WordCountMR /input2 /output2

然后输入hadoop fs -ls /可以看到output2文件夹,并且输入hadoop fs -ls /output2可以看到两个项目,如下图所示:

hadoop实例化出现问题 hadoop应用实例代码_hadoop实例化出现问题_06


hadoop实例化出现问题 hadoop应用实例代码_hadoop_07


java代码运行的结果就存放在part-r-00000文件中,可以使用cat /software/hadoop/bin/part-r-00000终端查看,也可以在网页端查看。

根据你自己配置的ip和端口输入,打开hdfs网页查看,例如我的配置是192.168.27.100:50070

hadoop实例化出现问题 hadoop应用实例代码_linux_08


打开文件系统查看:

hadoop实例化出现问题 hadoop应用实例代码_hadoop_09


点击output2查看:

hadoop实例化出现问题 hadoop应用实例代码_java_10


可以在线查看part-r-00000

还可以从hadoop服务器中移动到虚拟机中,然后通过FileZilla移动到本地计算机中查看
移动到虚拟机中的代码为:(要根据自己虚拟机的路径来移动)

#将服务器中的input2文件夹拷贝到虚拟机的/software/hadoop中
hadoop fs -copyToLocal /output2 /software/hadoop

hadoop实例化出现问题 hadoop应用实例代码_hadoop实例化出现问题_11


在虚拟机的output2中就可以查看part-r-00000文件了。

hadoop实例化出现问题 hadoop应用实例代码_hadoop_12