之前一直用hive处理数据,觉得MR程序打包上传的比较麻烦,后来偶遇hive搞不定的文件网上找了个MR的例子稍微改一下感觉也比较方便,主要是处理速度快。
MR程序主要是有3各类:main函数类,map重载类,reduce重载类。
第一步:maven里面添加几个jar包:
代码如下:
第二步:main类:主要是调度MR程序的启动运行
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
public class WordCount {
public static void main(String[]args)throws Exception
{
Configuration conf =new Configuration();//从hadoop配置文件读取参数
String [] otherArgs =new GenericOptionsParser(conf,args).getRemainingArgs();//从命令行读取参数
if(otherArgs.length!=2)
{
System.err.println("Usage:wordcount");
System.exit(2);
}
Job job =new Job(conf,"wordcount");
job.setJarByClass(WordCount.class);
job.setMapperClass(TokenizerMapper.class);
job.setReducerClass(IntSumReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job,new Path(otherArgs[0]));
FileOutputFormat.setOutputPath(job,new Path(otherArgs[1]));
System.exit(job.waitForCompletion(true)?0:1);
}
}
第三步:Map类:主要是按行读取文件内容,根据自己需要处理(默认按回车换行分割,如果要改动需要重载某个函数)
代码如下:
import java.io.IOException; //导入异常处理类
import org.apache.hadoop.io.IntWritable; //导入整数类
import org.apache.hadoop.io.Text; //导入文本类
import org.apache.hadoop.mapreduce.Mapper; //导入Mapper类
public class TokenizerMapper extends Mapper{
Text word = new Text(); //定义输出键
//统计每行数据每个id,aaa字符出现的次数
public void map(Object key,Text value,Context context)throws IOException,InterruptedException
{
int len = 0;
String id="";
len = appearNumber(value.toString(),"aaa"); //统计aaa出现次数
index = value.toString().indexOf("\"id\":\"");
if(index>0)
{
id = value.toString().substring(index+n,index+m); //取每行id值
IntWritable one = new IntWritable(len);
Text id_t = new Text(id);
context.write(id_t,one);
}
}
public static int appearNumber(String srcText, String findText) {
int count = 0;
int index = 0;
while ((index = srcText.indexOf(findText, index)) != -1) {
index = index + findText.length();
count++;
}
return count;
}
}
第四步:重载reduce类
代码如下:
import java.io.IOException;import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class IntSumReducer extends Reducer{
IntWritable result = new IntWritable();
public void reduce(Text key,Iterable values,Context context)
throws IOException,InterruptedException
{
int sum = 0;
for(IntWritable val:values) //不断地将values中的IntWritable整数提取出来给val
{
sum=sum + val.get();
}
result.set(sum);
context.write(key,result); //每个id的aaa出现的次数求和输出
}
}
第五步:打成jar包。上传hadoop,运行
hadoop jar mr_test1.jar WordCount hdfs:///myfile/test.log hdfs:///myfile/output10
PS:hdfs:///myfile/2018042319.log 为输入日志文件
hdfs:///myfile/output10 指定的输出目录
结果文件在:hdfs:///myfile/output10/part-r-00000
为了验证reduce阶段的作用,我曾把main函数中job.setReducerClass(IntSumReducer.class); 这句代码注掉,跑出的结果为每个id每行aaa出现的次数。将结果文件某个id grep出来,例如结果有17行,然后对17行的value求和,和加上job.setReducerClass(IntSumReducer.class)这句,这个id的结果完全一致。
MR程序的运行速度对比
MR程序的运行速度感觉比较快,像这个日志文件约10G,一开始的时候出于简单的想法写了一个shell脚本去处理,放在内存192G,CPU64核物理机运行,发现每秒大概只能处理不到1000条数据,算下来跑完需要约10小时,想shell的结果和mr的结果进行对比,所以就没有kill。如果只是慢也就算了,shell脚本执行完几个小时,运维忽然通知这台服务器重启了,也没有太详细的dump信息,说大概是内存不足。。。幸亏只是一台节点机,无其它定时任务,所以对集群无影响。估计是shell管理内存有硬伤,系统本身也有一些内存没来得及释放。
上面的MR程序运行在同样配置的物理机集群,约40台,10G文件运行也就几分钟,对比shell的结果基本相同,感觉速度快了不止一个数量级。