通过分析示例程序WordCount, 完成统计自己的网站日志需求——统计IP地址
前几天把事示例程序运行了一下,也算是成功的
今天就测试了用MapReduce来测试下分析网站的日志,主要统计来访的IP地址的次数
我们先分析一下WordCount 的源码——以我的认知
首先我们应该知道MapReduce处理文件是分而治之,不管你有多大的文件,我把你分成几个小的来处理,给其他的服务器来处理
至于如何分,以及给那一台服务器来处理,你不用去担心,这由MapReduce框架来处理
map类处理,reduce来汇总
你需要处理的就是重新写map和reduce方法
您还需要知道的就是hadoop的一些基本数据类型
这些数据类型都实现了WritableComparable接口
BooleanWritable:标准布尔型数值
ByteWritable:单字节数值
DoubleWritable:双字节数
FloatWritable:浮点数
IntWritable:整型数
LongWritable:长整型数
Text:使用UTF8格式存储的文本
NullWritable:当<key,value>中的key或value为空时使用
知道了以上的基本知识,下面我们看看源码
public class WordCount { public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>{ private final static IntWritable one = new IntWritable(1); private Text word = new Text(); public void map(Object key, Text value, Context context ) throws IOException, InterruptedException { StringTokenizer itr = new StringTokenizer(value.toString()); while (itr.hasMoreTokens()) { word.set(itr.nextToken()); context.write(word, one); } } } public static class IntSumReducer extends Reducer<Text,IntWritable,Text,IntWritable> { private IntWritable result = new IntWritable(); public 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 Exception { Configuration conf = new Configuration(); String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs(); if (otherArgs.length < 2) { System.err.println("Usage: wordcount <in> [<in>...] <out>"); System.exit(2); } Job job = Job.getInstance(conf, "word count"); job.setJarByClass(WordCount.class); job.setMapperClass(TokenizerMapper.class); job.setCombinerClass(IntSumReducer.class); job.setReducerClass(IntSumReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); 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])); System.exit(job.waitForCompletion(true) ? 0 : 1); } }WordCount 源码里有两个静态的内部类
TokenizerMapper和IntSumReducer
我们先看看TokenizerMapper静态内部类
TokenizerMapper继承了Mapper,并重写了map方法
map方法就是分析,比如你要统计每一个单词出现的个数,
你无需关心此时你运行的文件在那一台服务器,在那一行,你只需要知道,此时,给你的value就表示一行数据
而map里保存的key和value就是单词和单词出现次数的键值对
在源码中的这句context.write(word, one);
就表示work是key,而one是value
map就主要实现这个功能
以下代码为核心代码
StringTokenizer itr = new StringTokenizer(value.toString()); while (itr.hasMoreTokens()) { word.set(itr.nextToken()); context.write(word, one); }需要注意的是StringTokenizer类,这个类是在java.util包下
他有很多构造函数,他可以将一个字符串进行分割
然后调用其自身的hasMoreTokens对这个字符串行遍历,迭代输出
通过查询源码发现他有三个构造方法
本例中的构造方法new StringTokenizer(value.toString(),使用 \t\n\r\f 和空格来进行分割
更多关于StringTokenizer类的信息,可以参考以下连接
再看看IntSumReducer类
他继承了Reducer,并重写了reduce
reduce就是对map的结果进行一个汇总
我们也看到了里面的代码,就是遍历汇总
for (IntWritable val : values) { sum += val.get(); } result.set(sum); context.write(key, result);
最后在main函数中,设置一些基本的参数即可。
下面我说说我是如何统计我的网站日志中的IP的地址的
125.71.248.8 - - [28/May/2016:06:37:40 +0800] "GET /wp-content/themes/dux/css/bootstrap.min.css?ver=1.4 HTTP/1.1" 200 16238 "https://www.bugkong.com/" "Mozilla/5.0 (X11; Linux x86_64; CentOS Linux release 7.2.1511 (Core)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.0 Maxthon/1.0.5.3 Safari/537.36"代码为一条数据,事实上我的日志文件有接近6万多行,文件大小为12MB,试想一下,如果时间用传统的方法来统计,会怎么样。
上面的代码我以" - - " 进行分割。很明显,分割后的数组大小是2,第一个就是IP地址
那么我仅仅只需要修改一行代码即可
修改map中的一行代码
StringTokenizer itr = new StringTokenizer(value.toString());修改为:
StringTokenizer itr = new StringTokenizer(value.toString().split(" - - ")[0]);
这样我就实现了统计我最近一段时间内网站被访问的IP地址数据。
统计出来这段时间访问网站的IP地址高达5千多个,平均每个IP访问10页,最高访问25个,最低访问1次
爆款云服务器s6 2核4G 低至0.46/天,具体规则查看活动详情