分布式文档查重系统实现与思考
前言
为了学习分布式系统,于是做了这个项目来练练手,这篇文章记录了这次实战的思考和踩过的坑。由于刚入门,因而文章可能会有错误,请读者勘误。
简单概述
项目采用 Spark 和 HDFS 做分布式计算和存储,利用 Spring Boot 提供 Web 服务,利用 Redis 存储任务标识符以便于集群扩展。
用户上传包含 Word 文档的压缩文件,Web 服务器所在机器解压压缩文件,接着提取文档内容并分词,然后将结果存储至 HDFS 上,再利用 Spark 进行相似度计算并返回结果。
HDFS 依赖问题
搭建 HDFS 很简单只需做简单配置便可,但问题都出在操作文件的代码编写上。
先是依赖问题,官网及网上都没有具体 Hadoop 依赖说明(也可能自己没找到),导致对需要导入那些依赖包很疑惑,下面是自己测试的操作 HDFS 所需要的最小依赖。
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
HDFS 配置问题
获取 FileSystem
实例前需要创建一个 Configuration
实例,而在这之前必须设置本地 Hadoop 的目录,和设置远程机器的用户名。虽然这样做才能不报错,但一直不明白为什么操作远程机器的 HDFS,还需要本地安装的 Hadoop 目录。不过官方也提供了 HDFS 的 REST API,可以不依赖本地安装 Hadoop。
System.setProperty("HADOOP_USER_NAME", userName);
System.setProperty("hadoop.home.dir", home);
Configuration conf = new Configuration();
FileSystem.get(new URI("hdfs://host:port"), conf);
Spark 序列化问题
利用 Spark 进行计算时,最主要先获取 JavaSparkContext 的实例,以下为创建其实例的流程。
SparkConf conf = new SparkConf();
conf.setMaster("spark://host:port");
conf.setAppName("demo");
JavaSparkContext context = new JavaSparkContext(conf);
看似挺简单的流程,但问题就出在集群模式上。若是本地模式即指定 conf.setMaster("local")
进行调试,进行计算的代码可以完美运行,但是集群模式上就会出现序列化问题。
由于需要将代码分发给 Slave 节点的 Worker 进行计算,所以会产生代码的网络传输,而为了减少网络传输的延迟,Spark 只会分发部分代码,所以分发的代码若是不能进行序列化就会产生问题。
还有集群模式必须指定计算代码的 Jar 包路径,为了减少 Jar 的大小利于传输,采用了重新建立一个项目方案,然后在将该项目作为依赖导入主项目中,下面为利用计算代码中的 ComputingTask 来获取 Jar 包的路径。
String jarFile = ComputingTask.class.
getProtectionDomain().getCodeSource().getLocation().getFile();
context.addJar(jarFile);
Spark 数据源问题
数据源问题同样也是集群所导致的,本地模式下运行可以使用本地文件,但若是集群模式,由于 Slave 节点的 Woker 获取不到本地文件就会报错。因而数据源必须选择那些能远程使用的,例如分布式文件系统、数据库或者 FTP 服务器上的文件。
context.textFile("hdfs://host:port/test.txt"); // Don't use local file in cluster mode.
分词组件加载问题
项目中需要中文分词,所以选用了 Word 分词组件,但是每次头次加载都会花费很长时间,于是必须进行预加载,解决方案可以在该项目的 issue 中找到。
相似度计算
由于对该方面的算法了解不过,所以对文档的相似度计算,采用了简单地对两个文档进行杰卡德相似系数的计算,其定义为A与B交集的大小与A与B并集的大小的比值,下面为公式:
J(A,B) = |A ^ B| / |A v B|
项目计划
- 提供多种相似度的计算算法
- 提供 Dockerfile 方便环境的搭建
- 改造项目的简陋前端界面
- 解决无法清理临时文件问题
- 解决回收任务标识符问题
- 提供 Word 文档实时预览
项目地址为 https://github.com/linzhehuang/docdetect,觉得不错的话欢迎 Star !