十六、Hadoop之Java手动编写Map/Reduce从测试环境到生产环境

本文介绍了如何使用Java手动编写MapReduce程序,以统计歌曲艺人表中的艺人数量。首先,通过理解Hadoop自定义数据处理类的需求进行编程。接着,展示了程序示例,包括数据源和输出路径。最后,讨论了将程序打包到集群并运行的过程,以及在JavaWeb服务器中调用MapReduce任务的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ONE

标题利用一个WordCount稍微改动的小MapReduce程序来统计自己数据库下歌曲艺人表中的艺人数量来进行MapReduce的初入门,我利用的数据源来源于天池阿里竞技的赛题所提供的数据源。
歌曲明显是该表的唯一标识,艺人的字段明显可以存在复数个,现在假设需要统计艺人的数量:
在这里插入图片描述
在用java写MapReduce时需要注意,使用的不是jdk中自带的基本数据类型,需要使用对应hadoop自定义的数据处理框架的对应数据处理封装类,这样是为了更好的序列化数据。

 Long ---> LongWritable
 String ---> Text
 Integer ---> IntWritable
 Null ---> NullWritable

TWO

程序示例:

package person.ljj.msongs;

import java.io.IOException;
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 UserCount {
	
	/**对应的参数Mapper<keyin,valuein,keyout,valueout>,Mapper会自动读取自定定义的
	   输入文件的数据value,并自动产生一个偏移量作为key,在这里Object作为偏移量传入,
	   因为我这里的数据的偏移量数达到百万级,后续会达到亿级,因此我用Object让其自
	   动适应,第一个Text是指从文件传入的数据,第二个Text是Map输出的key类型,最后
	   一个参数就是输出的value类型*/
	private static class FieldSlicerMapper extends Mapper<Object, Text, Text, IntWritable> {
			
		private final static IntWritable one = new IntWritable(1);
		private Text field = new Text();
		
		//Context用来保存key-value和MapReduce集群的运行状况和某些参数,Context context只是简写,完全的的写法是这样的:Mapper<Object, Text, Text, IntWritable>.Context context
		@Override
		protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
			//通过"\r"来将的数据库的数据分隔成一行行,这也是默认的方式,可以不用该参数也可以,或不用这行也行,因为
			//map默认每次读取的是一行,value传进来的就是默认的一行数据,想要一次多行或更多的自定义读取格式需要手动需改InputFormat
			StringTokenizer itr = new StringTokenizer(value.toString(),"\r");
			while(itr.hasMoreTokens()) {
				String[] fields = itr.nextToken().split(",");//每一行按照“,”来split成数组
				field.set(fields[1]);//提取每一行中艺人字段的数据
				context.write(field, one);//上下文保存艺人名和one:key->value == ArtistName -> 1,这里为啥是保存1?和wordCount原理一样的,为了在reduce阶段更好的统计Artist的数量
			} 
		}
	}
		

	private static class UserSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
		
		private IntWritable result = new IntWritable(); 

		/**这里的传入参数对应Map中的context.write中的参数,为啥是values?因此在reduce前已经经历过一个隐藏阶段Combiner,
		   自动将重复的key柔合了,要是存在一大堆重复的key我才觉得奇怪,就像一叠一样的身份证对应的都是不一样的人?。。。
		   那一张身份证对应一堆不一样的人??,这种状况我就会换个说法,见机行事,机智(•́⌄•́๑)૭✧,value自然在程序中就变
		   成了values*/
		@Override
		protected void reduce(Text key, Iterable<IntWritable> values,
				Context context) throws IOException, InterruptedException {
			int sum = 0;
			for(IntWritable val:values) {
				sum += val.get();
			}
			result.set(sum);
			context.write(key, result);
		}
	}
	
	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
		Configuration conf = new Configuration();
		String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
		if (otherArgs.length < 2) { //输入元可能存在多个,输出源只有一个,因此length >= 2
			System.err.println("Usage: usercount <in> [<in>...] <out>");
			System.exit(2);//System.exit(status)表示返回最上层,常见的return代表返回上一层,status为0时表示正常退出,其它参数都为非正常退出
		}
		Job job = Job.getInstance(conf,"user count");
		job.setJarByClass(UserCount.class);//那个程序引用map/reduce 的class就填那个,这里为self
		job.setMapperClass(FieldSlicerMapper.class);
		job.setCombinerClass(UserSumReducer.class);//Combiner阶段默认在Reduce中,如果没有手动作更多的变动的话
		job.setReducerClass(UserSumReducer.class);
		job.setOutputKeyClass(Text.class);//设置输出的key的数据类型string对应Text,这里传入Text.class
		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]));
		//Job运行是通过job.waitForCompletion(true),true表示将运行进度等信息及时输出给用户,false的话只是等待作业结束
		System.exit(job.waitForCompletion(true) ? 0 : 1);//开始执行程序
	}
}

running:

hdfs://master:8020/user/hive/warehouse/msongsdb.db/t_songs/t_songs.csv hdfs://master:8020/tmp/tianchi/output2
在这里插入图片描述
注意到这个/msongsdb.db/t_songs/t_songs.csv 了没,msongsdb.db是一个数据库文件,t_songs.csv就是一个csv文件,却保存在msongsdb.db中,中间的t_songs是表名,说明了一个将hdfs中的csv文件转移到hive下的数据库中(也是在hdfs上)是直接通过将文件剪切粘贴到table下的

THREE

将程序打为jar包,放到集群的$HADOOP_HOME/share/hadoop/mapreduce文件夹中,通过Linux的控制台跑一下
在这里插入图片描述
在这里插入图片描述
注意调用类的时候自定义的包hadoop无法自动搜索到相关的class需要把包名给带上,到此从测试到生产整个流程就通了哦,怎么在JavaWeb服务器中这样做?当然是想方设法去通过hadoop的API来调用自定义的MapReduce的Jar达到我们的目的啊,待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值