![timg](https://i-blog.csdnimg.cn/blog_migrate/844c583e7aef4dcb291cef1f16507e53.png)
经过同学们的反馈,今天的内容还是比较多的
在此记录下最主要的两个案例
-
案例一-日志清洗
今晚的一个任务是使用MapReduce完成一个日志文件清洗,在此记录下清洗过程
日志数据格式:
101.226.33.222 - - [31/Mar/2015:23:59:59 +0800] "GET /uc_server/data/avatar/000/07/52/20_avatar_middle.jpg HTTP/1.1" 200 5043
![实训4-1](https://i-blog.csdnimg.cn/blog_migrate/e05fd77c3de44ed7d4dd2be5edcaef97.png)
红框所圈住的内容为要求提取出的内容
1. 分析正则表达式
提取文本中指定格式内容,利用正则表达式再好不过了。
正则表达式教程:正则表达式-教程 | 菜鸟教程
根据日志数据格式得出匹配每一行的正则表达式一般为
.* - - \[.* \+0800\] "(GET|POST) .* HTTP/1.1" \d+ .+
- 分析过程:
可以利用Notepad++ ,为便于利用正则表达式测试,首先复制日志一小部分内容到一个新文本文件(否则文件过大,软件处理得会很慢)
Notepad++ 中按Ctrl+F键,勾选“正则表达式”,可以通过正则查找匹配文本,我们可以利用这个功能进行正则匹配测试。
首先大致可以看出匹配的正则表达式一般是
.* - - \[.* \+0800\] "GET .* HTTP/1.1" \d+ \d+
搜索框中输入上面的正则表达式,不断点击“查找下一个”,可以观察到它所匹配的每一行。
在本文件中,因第13行最后一个字段并非“\d+”匹配的数字,所以会跳过。点击“计数”可以查看有多少行匹配。根据这些结果,不断对正则表达式进行调整,最终结果为
.* - - \[.* \+0800\] "(GET|POST) .* HTTP/1.1" \d+ .+\
对于较小的数据量来说,实际上可以通过Notepad++ 中的替换功能就可以得到本案例所要求的结果。
2.创建MapReduce任务
下面来编写JAVA代码,为便于学习,这里我完全使用Windows。
请参考:《如何使用Windows进行MapReduce编程》
首先创建MapReduce项目(File→New→Project→如图)
![实训4-3](https://i-blog.csdnimg.cn/blog_migrate/7af0c56b4080cfdd70c06642cdc93f0e.png)
编写代码:
// LogClean.java
package logclean;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
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;
public class LogClean {
public LogClean() {
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
String[] otherArgs = {"input","output"};
Job job = Job.getInstance(conf, "log clean");
job.setJarByClass(LogClean.class);
job.setMapperClass(LogClean.LogMapper.class);
job.setCombinerClass(LogClean.LogReducer.class);
job.setReducerClass(LogClean.LogReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.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]));
job.waitForCompletion(true);
}
public static class LogReducer extends Reducer<Text, Text, Text, Text> {
public LogReducer() {
}
public void reduce(Text key, Iterable<Text> values, Reducer<Text, Text, Text, Text>.Context context) throws IOException, InterruptedException {
// 直接将每一行文本作为Key,空文本作为value输出
context.write(key, new Text(""));
}
}
public static class LogMapper extends Mapper<Object, Text, Text, Text> {
private Text word = new Text();
public LogMapper() {
}
public void map(Object key, Text value, Mapper<Object, Text, Text, Text>.Context context) throws IOException, InterruptedException {
// 每一行进行正则表达式替换
String line = value.toString().replaceAll("(.*) - - \\[(.*) \\+0800\\] \"(GET|POST) (.*) HTTP/1.1\" (\\d+) (.+)", "$1,$2,$4,$5,$6");
this.word.set(line);
// 输出替换后的每一行<line,>
context.write(this.word, new Text(""));
}
}
}
上面代码中replaceAll()将正则表达式匹配的内容替换为其他字符串。正则表达式中,每一个小括号代表一个捕获组,可以在第二个参数中通过$再次取用。
3.执行
将两个日志文件放在src/input目录下(input需手动创建)
log4j.properties放在src目录下
然后运行LogClean.java
在src/output下可以看到生成的两个文件
![实训4-4](https://i-blog.csdnimg.cn/blog_migrate/e931178995805ce11822e871c202c707.png)
执行用时:88992ms
-
案例二-查找共同好友
以下是博客的好友列表数据,冒号前是一个用户,冒号后是该用户的所有好友(数据中的好友关系是单向的)
A:B,C,D,F,E,O
B:A,C,E,K
C:F,A,D,I
D:A,E,F,L
E:B,C,D,M,L
F:A,B,C,D,E,O,M
G:A,C,D,E,F
H:A,C,D,E,O
I:A,O
J:B,O
K:A,C,D
L:D,E,F
M:E,F,G
O:A,H,I,J
求出哪些人两两之间有共同好友,及他俩的共同好友都有谁?
1.首先求出每个人都是谁的好友
程序代码如下:
// FdMR1.java
package friend;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class FdMR1 {
public static class FdMapper extends Mapper<LongWritable, Text, Text, Text> {
public void map(LongWritable ikey, Text ivalue, Context context) throws IOException, InterruptedException {
// 第一个字符代表一个人
String one = ivalue.toString().substring(0, 1);
String[] following = ivalue.toString().substring(2).split(",");
for(String fo:following) {
// 输出 <Who, Who是谁的好友>
context.write(new Text(fo), new Text(one));
}
}
}
public static class FdReducer extends Reducer<Text, Text, Text, Text> {
public void reduce(Text _key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
// process values
StringBuffer sb = new StringBuffer("");
for (Text val : values) {
sb.append("," + val.toString());
}
context.write(_key, new Text(sb.toString()));
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "friend");
job.setJarByClass(FdMR1.class);
job.setMapperClass(FdMapper.class);
job.setReducerClass(FdReducer.class);
// TODO: specify output types
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
// TODO: specify input and output DIRECTORIES (not files)
FileInputFormat.setInputPaths(job, new Path("input"));
FileOutputFormat.setOutputPath(job, new Path("out"));
if (!job.waitForCompletion(true))
return;
}
}
2.然后将每两个人的共同好友输出
程序代码如下:
//FdMR2.java
package friend;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class FdMR2 {
public static class FdMapper extends Mapper<LongWritable, Text, Text, Text> {
public void map(LongWritable ikey, Text ivalue, Context context) throws IOException, InterruptedException {
// 这个人是后面几个人的好友
String one = ivalue.toString().substring(0, 1);
System.out.println(ivalue.toString());
// 后面几个人的好友为one
String[] follower = ivalue.toString().substring(3).split(",");
for(String fo:follower)
{
System.out.println(fo);
}
for(int i = 0;i < follower.length - 1;i++)
for(int j = i + 1;j < follower.length;j++)
{
System.out.println(follower[i] + "-" + follower[j] + " " + one);
if (follower[i].charAt(0) < follower[j].charAt(0)) {
context.write(new Text(follower[i] + "-" + follower[j]), new Text(one));
}else {
context.write(new Text(follower[j] + "-" + follower[i]), new Text(one));
}
}
}
}
public static class FdReducer extends Reducer<Text, Text, Text, Text> {
public void reduce(Text _key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
// process values
StringBuffer sb = new StringBuffer("");
for (Text val : values) {
sb.append("," + val.toString());
}
context.write(_key, new Text(sb.toString()));
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "friend");
job.setJarByClass(FdMR2.class);
job.setMapperClass(FdMapper.class);
job.setReducerClass(FdReducer.class);
// TODO: specify output types
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
// TODO: specify input and output DIRECTORIES (not files)
FileInputFormat.setInputPaths(job, new Path("out"));
FileOutputFormat.setOutputPath(job, new Path("out2"));
if (!job.waitForCompletion(true))
return;
}
}