目的及要求
给定一个图<V,E>,基于MapReduce设计实现最小生成树算法,找到给定图的最小生成树。搭建的Hadoop开发环境,至少包含一个name node和一个data node。
实验环境
运行环境:Ubuntu18.04、开发IDE:IDEA
实验内容与步骤
基于docker搭建Hadoop集群环境
Hadoop目前版本已更新到V3,指令和端口与V2相比有不少变化。
| 端口名称 | Hadoop 2.x | Hadoop 3.x |
|---|---|---|
| NameNode内部通信端口 | 8020/9000 | 8020/9000/9820 |
| NameNode HTTP UI | 50070 | 9870 |
| MapReduce查看执行任务端口 | 8088 | 8088 |
| 历史服务器通信端口 | 19888 | 19888 |
本文使用docker compose对Hadoop各组件进行编排,可满足自定义和快速部署测试的需求,节点角色和主机映射关系如下表。
| hadoop-master | hadoop-slave1 | hadoop-slave2 | |
|---|---|---|---|
| HDFS | NameNode、SecondaryNameNode | DataNode | DataNode |
| YARN | ResourceManager、LogServer | NodeManager | NodeManager |
在原有Github项目的基础上,本文做了以下工作:
① 升级Hadoop版本为官网最新版本3.2.4,处理参数名称变更,配置文件重命名等问题。
② 原有项目是通过docker命令行执行启动,不方便暂停容器、重启容器等操作,本文通过docker compose对容器进行编排,设置挂载目录和端口映射信息,将docker-compose.yml与Dockerfile文件进行联动,方便后续升级。
③ 添加历史日志聚合功能,查看Mapper、Reducer数量和执行时间。
首先从Hadoop官网下载最新安装包,放入与Dockerfile同级目录下,在原始镜像Ubuntu18.04的基础上,安装openssh-server、openjdk-8-jdk,国内可通过清华源进行加速。
替换ssh的配置文件ssh_config如下,即不对新的ssh连接进行公钥确认。
Host localhost
StrictHostKeyChecking no
Host 0.0.0.0
StrictHostKeyChecking no
Host hadoop-*
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null
同时将ssh产生的公钥加入到已知密钥中,因为开发环境下,多个Hadoop节点会共用一个镜像,所以远端的ssh密钥已经添加到authorized_keys文件中,认证通过,Dockerfile中相关命令如下。
RUN ssh-keygen -t rsa -f ~/.ssh/id_rsa -P '' && \
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
在Hadoop配置文件中指明节点角色,需要修改的配置文件和内容如下:
| 配置文件名称 | 配置项 | 配置说明 |
|---|---|---|
| hadoop-env.sh | JAVA_HOME | JAVA路径 |
| core-site.xml | hadoop.tmp.dir、fs.defaultFS | 数据存储目录、NameNode地址 |
| hdfs-site.xml | dfs.namenode.name.dir、dfs.datanode.data.dir、dfs.replication | Log日志路径、数据路径、数据副本数 |
| mapred-site.xml | mapreduce.framework.name、mapreduce.jobhistory.address、mapreduce.jobhistory.webapp.address | 配置yarn作为底层控制、内部历史服务器地址、外部历史服务器地址 |
| yarn-site.xml | yarn.nodemanager.aux-services、mapreduce_shuffle.class、yarn.resourcemanager.hostname、yarn.log-aggregation-enable、yarn.log.server.url、yarn.log-aggregation.retain-seconds | 配置 NodeManager 上运行的服务、mapreduce启动类设置、ResourceManager节点地址、开启日志聚合、yarn的聚合日志地址、日志保留时间 |
| workers | hadoop-slave1hadoop-slave2 | 每行的主机上都会运行DataNode和NodeManager |
在Dockerfile中通过COPY、RUN等原语拷贝配置文件到工作目录,执行hdfs namenode -format初始化HDFS。
接下来在docker-compose.yml配置端口映射、目录映射信息,Hadoop所有节点加入名为hadoop的独立网络,避免地址冲突。需要映射的端口为9870、8088、19888,ssh端口不需要映射,用户通过docker exec命令进入容器内执行命令,另外由于运行docker compose时镜像还未构建,可以在docker-compose.yml配置自动构建的语句,新构建的基于Docker-compose的Hadoop部署链接。
hadoop-master:
image: hadoop-toy:1.0
build:
context: .
dockerfile: Dockerfile
......
hadoop-slavex:
......
depends_on:
- hadoop-master
最小生成树
在一个具有N个顶点的带权连通图G中,如果存在某个子图G’,其包含了图G中的所有顶点和一部分边,且不形成回路,并且子图G’的各边权值之和最小,则称G’为图G的最小生成树。
常用的最小生成树算法有Kruskal、Prim、Boruvka。
Kruskal的核心思想是它直接选边,在找最小生成树结点之前,需要对所有权重边做从小到大排序,将排序好的权重边依次加入到最小生成树中,如果加入时产生回路就跳过这条边,当所有结点都加入到最小生成树中之后,就找出了最小生成树。而Prim算法操作的对象是结点,它首先以一个结点作为最小生成树的初始结点,然后以迭代的方式找出与最小生成树中各结点权重最小边,并加入到最小生成树中,加入之后如果产生回路则跳过这条边。最后Boruvka算法的操作对象则是集合,多个连通结点构成一个集合,算法描述如下:
① 输入一个连通的有向带权图。
② 把所有顶点都初始化成单独的一个子集。
③ 初始化一个空的MST。
④ 只要子集个数大于1,就对依次各个子集合执行以下操作:找出所有与当前集合有边相连的集合,选出权重最小的那条边(设为minEdge);如果minEdge不存在于MST中,则把minEdge添加到MST中。
⑤ MST求得,算法结束。
本文首先基于MapReduce在shuffle阶段自动按键排序的特点,测试基于Kruskal算法得到最小生成树,程序逻辑如下。
先将输入文件存入HDFS。
然后把IDEA打包好的jar包拖入共享文件夹内,选择不带依赖的版本即可,远端已经具备依赖包。
然后在命令行输入以下命令进行测试。
hadoop jar HadoopApp.jar lkh.hadoop.demo.MST mediumEWG.txt output 2 1
其中第一个参数为输入文件路径,第二个参数为输出文件路径,第三个参数为mapper阶段的切割单元,单位为KB,根据此项设置mapper的数量,第四个参数为reducer的数量。运行程序时必须保证输出文件夹不存在,如已存在,可使用hdfs dfs -rm -r ouput命令删除。
在历史日志服务器查看执行日志。
但是经测试,发现只有在单个Reducer时,该最小生成树算法是有效,结果如下。
当多于1个reducer时,算法无法维护全局的节点连接信息,最终得到的边数显然不符合最小生成树要求。
Karlof等人[2,3]引入新的最小生成树算法可以在多reducer情境下进行,算法思想是多个局部最小生成树能构成全局最小生成树,2个mapreduce阶段作为1个处理回合,整体算法逻辑如下图所示。
实验结果与数据处理
接下来测试Karlof算法性能在mapreduce环境的影响因素。
在mapper数量恒定为1的情况下,运行时间和reducer数量、图规模关系如下图所示。
在reducer数量恒定为1的情况下,运行时间和mapper数量、图规模关系如下图所示。
通过以上数据可以发现,在最小生成树场景下,程序的耗时主要在mapper阶段,并行化reduce不会带来很多提升,另外更多的mapper、reducer意味着更多的程序切换开销,所以出现更多reducer反而耗时更多的状况。在reducer数量一定时,数据量更大的计算受mapper影响更大,这也是Hadoop设计的主要目的。
分析与讨论
本文对MapReduce环境下的最小生成树算法进行了讨论,常见的单机算法并不适合MapReduce模式,因为数据总是分散在多个Reducer节点上,无法实现全局的搜索。研究人员提出的新算法[3,4]通过冗余边信息,实现从局部解推出全局解的目标,本文最后对其中的Karlof算法进行测试,发现MapReduce更适合大数据量、耗时长等任务,但是该实验也存在缺陷,算法最后会调整reducer数量,将经过前序筛选后的节点放入同一计算reducer,加快生成树速度。
参考文献:
[1] guillermobet. MapReduceMST. github.com/guillermobe…. Accessed July 14, 2022.
[2] ganeshskudva. MinimumS panningT ree. https : / / github . com / ganeshskudva / Minimum _ Spanning _ Tree.Accessed July 14.
[3] Karloff H, Suri S, Vassilvitskii S. A model of computation for mapreduce[C]//Proceedings of the twenty-first annual ACM-SIAM symposium on Discrete Algorithms. Society for Industrial and Applied Mathematics, 2010: 938-948.
[4] Lattanzi S, Moseley B, Suri S, et al. Filtering: a method for solving graph problems in mapreduce[C]//Proceedings of the twenty-third annual ACM symposium on Parallelism in algorithms and architectures. 2011: 85-94.