综合项目(三):查找QQ共同好友
3.1 项目分析
3.1.1 项目功能简介
社交网络中的好友是指在社交网络中出现在用户联系人列表中的用户,它是一种广义上的朋友,既可以是Facebook和街旁网中的好友,也可是新浪微博中的关注用户等。为社交网络用户推荐好友就是帮助用户在社交网络中找到她们感兴趣的用户,进而添加到自己的联系人列表之中。
在社交网络中存在大量稀疏的朋友关系,为用户提供准确、快速的好友推荐服务是一项极具挑战性的任务。当前,社交网络中的好友推荐通常利用三种用户信息:用户相似度信息、网路结构信息和内容信息。
目前的社交网络平台大都通过用户的属性信息来计算两个用户的相似度。两个用户的相似度越大,则他们之间越有可能成为好友,进而向他们彼此进行好友推荐。
在社交网络中,用户之间的好友关系在不断地建立和丰富,以此形成的社交网络结构也更加真实可靠。利用网络结构信息,采用不同的指标来度量用户的相似性,这样可以产生更好的好友推荐结果,让用户发现很多的熟人。
用户发布的内容信息,如评论信息、回复内容、用户标签、图片、视频等,在一定程度上与用户所关系的问题有关,研究人员已经开始探索利用各种内容信息,来为用户提供准确的好友推荐服务。
本项目使用Java语言编写MapReduce程序,帮助用户找出哪些人两两之间有共同好友,及他俩的共同好友都有谁。初始数据如图3.1所示,运行结果如图3.2所示。
图3.1 用户好友数据原始数据
图3.2 两两之间的共同好友
3.1.2 项目模块分析
根据“查找共同好友系统”程序的执行效果可知,该项目分为2个大模块,如图3.3所示。
图3.3 查找共同好友系统模块
(1)查找好友隶属:编写MapReduce程序,查找每个人的好友隶属关系,查找好友隶属于哪些人。
(2)查找共同好友:根据好友的隶属关系,查找两两的共同好友。
3.1.3 模块业务流程分析
1.对“查找好友隶属”的执行效果进行分析,业务流程如下。
(1)上传原始数据到HDFS的指定目录中。
(2)上传MapReduce程序到集群,使用命令行的方式执行该程序,查找每个人的好友隶属关系,并将隶属关系存放到HDFS的指定目录。
2.对“查找共同好友”的执行效果进行分析,业务流程如下。
(1)上传MapReduce程序到集群,使用命令行的方式执行该程序,将好友隶属关系文件作为数据源,进行两两的共同好友处理。
(2)到输出的结果目录下载结果数据,验证结果的正确性。
3.1.4项目涉及技术
使用Hadoop集群,并编写MapReduce,对好友原数数据进行处理,到两两的共同好友。
3.1.5 项目计划
“商品推荐系统”项目总开发课时数10课时,具体任务计划表见表3.1。
表3.1 任务计划表
序号 | 任务 | 课时数 |
1 | 讲解项目分析、数据模型以及程序结构设计 | 2 |
2 | 讲解并完成查找好友隶属关系的MapReduce编程 | 4 |
3 | 讲解并完成查找两两共同好友的MapReduce编程 | 4 |
3.2 项目程序结构的设计和实现
3.2.1 文本数据的设计和实现
根据分析“查找共同好友”项目运行效结果,可知本项目需要描述,并存储三种信息:用户好友信息文本、好友隶属关系文本和两两共同好友文本,每种信息由多个属性构成。本项目设计使用文本存放这些信息,其中分别设计好友原始数据文本、好友隶属关系文本和两两共同好友文件来存放这些信息。
1.好友原始数据文本
好友原始数据文本主要保存好友原始数据。好友原始数据结构的设计见图3.4所示。
冒号左边为用户,冒号右边为好友列表,好友以逗号隔开。 |
图3.4 好友原始数据
2.好友隶属关系文本
好友隶属关系文本主要保存好友隶属关系数据。好友隶属关系数据结构设计见图3.5所示。
制表符左边为人物为右边人员的隶属的人物。 |
图3.5 好友隶属关系文本数据
3.两两共同好友文本
制表符左边为两人,右边为两人的共同好友。 |
两两共同好友文本主要保存两两共同好友数据。两两共同好友数据结构设计见图3.6所示。
图3.6 两两共同好友数据
3.2.2 程序结构的设计和实现
1.项目设计
根据功能需求分析,设计如表3.2所示的二个模块。
表3.2 商品推荐系统子项目
序号 | 项目名称 | 说明 |
1 | ShareFriendsOne | 使用Hadoop的JavaAPI编写,MapReduce程序,分析好友的隶属关系。 |
2 | ShareFriendsTow | 使用Hadoop的JavaAPI编写,MapReduce程序,根据好友隶属关系查找共同好友。 |
2.3 MapReduce分析程序的设计和实现
2.3.1 MapReduce业务流程分析
在“大数据平台”上执行MapReduce。
1.好友隶属关系数据转换,执行流程如下:
(1)执行Mapper对象,读取文本中的好友数据,处理完成后发送至Reducer对象。
(2)执行Reducer对象,接受Mapper发送的数据,将结果存放到HDFS指定目录。
2.查找共同好友,执行流程如下:
(1)执行Mapper对象,读取转换后的隶属关系数据,处理完成后发送至Reducer对象。
(2)执行Reducer对象,接受Mapper发送的数据,将数据进行处理,并结果存放到 HDFS指定目录。
2.3.2 MapReduce具体实现
1.将好友数据转换为好友隶属于哪些人,并存放到HDFS指定目录,程序代码如下:
- 编写Mapper对象的子对象,代码如下:
static class ShareFriendsMapper extends Mapper<LongWritable,Text,Text,Text> {
@Override
protected void map(LongWritable a, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] person_friends = line.split(":");
String friends = person_friends[1];
String person = person_friends[0];
for (String friend:friends.split(",")){
context.write( new Text(friend),new Text(person));
}
}
- 编写Reducer对象的子对象,代码如下:
static class ShareFriendsReducer extends Reducer<Text,Text,Text,Text>{
@Override
protected void reduce(Text friend, Iterable<Text> persons, Context context) throws IOException, InterruptedException {
StringBuilder sb = new StringBuilder();
for (Text person:persons){
sb.append(person).append(",");
}
context.write(friend,new Text(sb.toString()));
}
}
- 创建Driver封装MapReduce作业类的相关方法,代码如下:
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
conf.set("mapreduce.framework.name", "yarn");
conf.set("yarn.resourcemanager.hostname", "master");
conf.set("fs.defaultFS", "hdfs://master:9000/");
Job job=Job.getInstance(conf);
//设置jar
job.setJarByClass(ShareFriendsOne.class);
// 指定mapper类和reducer类
job.setMapperClass(ShareFriendsMapper.class);
job.setReducerClass(ShareFriendsReducer.class);
// 指定maptask的输出类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
// 指定reducetask的输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
// 指定该mapreduce程序数据的输入和输出路径
Path inputPath = new Path("/data");
Path outputPath = new Path("/output");
// 如果输出路径存在,删除之
outputPath.getFileSystem(conf).delete(outputPath, true);
FileInputFormat.setInputPaths(job, inputPath);
FileOutputFormat.setOutputPath(job, outputPath);
boolean waitForCompletion = job.waitForCompletion(true);
System.exit(waitForCompletion?0:1);
}
运行结果,如图3.7所示:
执行首个MapReduce所得到的数据(好友隶属关系)。 |
图3.7 运行首个MapReduce得到的数据
2.查找两两的共同好友,并存放到HDFS指定目录,程序代码如下:
- 编写Mapper对象的子对象,代码如下:
static class ShareFriendsTowMapper extends Mapper<LongWritable,Text,
Text,Text>{
Text key = new Text();
Text out = new Text();
@Override
protected void map(LongWritable a, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] friend_persons = line.split("\t");
String friend = friend_persons[0];
String[] persons = friend_persons[1].split(",");
Arrays.sort(persons);
for (int i=0;i<persons.length-1;i++){
for (int j=i+1;j<persons.length;j++){
key.set(persons[i]+"+"+persons[j]);
out.set(friend);
context.write(key,out);
}
}
}
}
- 编写Reducer对象的子对象,代码如下:
static class ShareFriendsTowReducer extends Reducer<Text,Text,Text,Text> {
@Override
protected void reduce(Text friend, Iterable<Text> persons, Context context) throws IOException, InterruptedException {
StringBuilder sb = new StringBuilder();
for (Text person:persons){
sb.append(person).append(",");
}
context.write(friend,new Text(sb.toString()));
}
}
- 创建Driver封装MapReduce作业类的相关方法,代码如下:
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
conf.set("mapreduce.framework.name", "yarn");
conf.set("yarn.resourcemanager.hostname", "master");
conf.set("fs.defaultFS", "hdfs://master:9000/");
Job job=Job.getInstance(conf);
//设置jar
job.setJarByClass(ShareFriendsTow.class);
// 指定mapper类和reducer类
job.setMapperClass(ShareFriendsTowMapper.class);
job.setReducerClass(ShareFriendsTowReducer.class);
// 指定maptask的输出类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
// 指定reducetask的输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
// 指定该mapreduce程序数据的输入和输出路径
Path inputPath = new Path("/output/part-r-00000");
Path outputPath = new Path("/result");
// 如果输出路径存在,删除之
outputPath.getFileSystem(conf).delete(outputPath, true);
FileInputFormat.setInputPaths(job, inputPath);
FileOutputFormat.setOutputPath(job, outputPath);
boolean waitForCompletion = job.waitForCompletion(true);
System.exit(waitForCompletion?0:1);
}
运行结果,如图3.7所示。
图3.7 两两之间的共同好友