对Giraph的一些理解

Giraph的一些理解

这两天又重新看了一下Giraph源码,对整体架构的理解又有了新的认识和理解,下面逐点来说。

一、          Giraph本质的理解:

大家都知道,Giraph对用户来讲可能是一个基于Pregel模型的图运算项目,但是对于Hadoop来讲,其实它是一个普通的MapReduce任务。因此我们在运行时可以把他看成是一个mapreduce任务,只是这个任务有点特殊和复杂。

特殊在,它没有像普通mapreduce任务一样重写mapreduce函数,而只是写了run函数,所以一切逻辑都在run函数中呈现,mapreduce函数都是空函数,并且这个对于hadoopjob没有reduce任务,只有map任务。

复杂在,它不是简单的map任务,在run函数中实现了一切pregel逻辑,包括数据加载、图数据重分区、图数据的计算等。

二、          Giraph到底利用了hadoop的什么?

首先,用到mapreduce框架,是不是感觉有点蒙圈了?通过第一点的讲解,大家知道了Giraph的本质,但是换个角度想想,它其实只是用到了hadoop的分布式任务启动的功能,也就是说,hadoop给这个特殊的Job启动了Nmaptask以后就再也没做什么。

             那么问题来了,使用过Giraph的伙伴们都知道,在运行命令里我们可以指定work的数数目(-w N),而且神奇的发现,hadoop确实启动了N+1(多出来的一个是master)个mapworker)任务,可能有人会说,我们在使用hadoop的可以指定map数量,嗯嗯,这个说法没错,难道这么简单就完了吗?仔细想想,用户指定的hadoop数目不一定能起到作用啊,hadoop在确定map任务数量时候是根据它在hdfs上的block数量来启动的,默认每个block启动一个map任务,所以当我们在giraph启动时人工指定的map数目不一定起作用。其实giraph用了一个很聪明的办法,重写InputFormat啊,在giraph中它为hadoop指定的InputFormat是一个“空”的-----BspInputFormat,这个BspInputformat中一个重要的方法getsplits()其实获得的是含有一个空路径的splitlist,但是他的数量是N+1。所以hadoop在确定启动多少个maptask时待用这个方法得到了含有N+1splitlist,启动了N+1maptask来作为Giraphworker,但是split中文件路径为空,所以Hadoop没有加载任何数据。

             其次,giraph确实用到了hadoophdfs,这个没什么说的。

三、          Giraph同步和通信的理解

可能你又会问,Giraph这个框架要实现Pregel必须满足同步机制和通信等功能,如何实现的呢?

首先来说说通信吧,在Giraph中,因为它本质上是map任务,但没有用到mapreduce框架的任何通信功能,它的所有通信都是用的Netty这个apache开源分布式通信工具完成的,也就是不同worker之间的消息(包括节点消息等)都是netty完成的。

其次是同步机制,学习hadoop生态圈的对zookeeper都不会陌生,zookeeper是一个类似于简单分布式文件系统的分布式协同管理工具(具体的内容自己看吧)。在Giraph中,每一个迭代步中worker都会在zookeeper上某个路径下(设为A)建立相应迭代步的znode,并且监听“本步迭代完成标志节点”是否已经创建,master的任务就是不停检查A路径下的znode节点数目是否等于worker数目,若等于则创建“本步迭代完成标志节点”,此时worker会监听到,进入下一个迭代步。

四、          Giraph的这个工作流程的理解

这个本来应该画一个流程图来说明更加直观的,由于一些原因先文字叙述一下,后期再补吧。

通过第二点可以知道,当GiraphJob提交以后,hadoopGiraph启动了N+1个任务后(此时的任务叫做maptask,因为还没有确定哪个maptaskmaster哪个是worker),那么Giraph接下来都做了哪些内容呢?

1.  GIraph要确定集群上有没有zookeeper服务(用户可以指定),如果有,则进入第2步,如果没有此时要启动zookeeper服务。熟悉zookeeper的小伙伴应该知道,zookeeper中应该有一个主服务器和多个从服务器来为(client)集群服务,因此Giraph首先要启动zookeeper服务器,Giraph中默认启动一个zookeeper服务器。这个过程又是怎样的呢?

1.1      启动好的多个maptask首先在hdfs的指定路径下创建本task的标志(例如,路径B/host1 taskID)

1.2      每个task启动一个zookManage(类名可能不正确,详细见源码)对象,这个对象的主要任务就是从这些个maptask中选取一个作为zookeeper服务器,经验证这个始终都是task0,有兴趣的可以自己看下源码。

1.3      Task0zookeeper服务器启动,并在hdfs上挂出服务器地址和端口,其他task读取地址和端口,所有task进入第2步。

2.          Giraph启动了zookeeper服务以后,接下来要做的就是确定每个task的角   色,也就是确定它具体是master还是worker,判断逻辑如下:

a)      If not split master, everyone does the everything and/or runningZooKeeper. 

b)      If split master/worker, masters also run ZooKeeper

c)      If split master/worker == true and giraph.zkList is set, the masterwill not instantiate a ZK instance, but will assume a quorum is already activeon the cluster for Giraph to use.

最终的结果就是,作为zookeeper服务器的task0兼任master,这个task不参与具体运算。其他的taskworker,参与具体运算。

3. 接下来,masterworker开始做不同的事情,并且开始进行同步了,这个过程利用了刚刚建好的zookeeper服务来完成。

master要做的事情就是加载用户输入文件的splits信息,这个信息是利用用户定义的InputFormat(注意区分BspInputFormat)来获取的,此时获取的splits才是真正的数据,master会将splits信息以znode节点的形式挂到zookeeper上,同时master根据splits信息创建分区,这里需要说明一下分区数和splits数目,splits数目一般是hdfs上的blocks(这个和hadoop相似)的数量,分区数目则不同,分区数目是通过系统的一个计算公式(partitionCount=PARTITION_COUNT_MULTIPLIER* availableWorkerInfos.size() * availableWorkerInfos.size())来创建,这个数目要比workers数目大,得到这个数目后,会将分区安排到不同的work上,并将该partionToWorker这个信息也以znode的形式挂到zookeeper上。

            其他的workermaster生成splits信息和partion信息之前一直处于同步等待阶段,当master将信息挂载到zookeeper后,workerzookeeper上读取splits信息,并且开启线程进行数据加载,数据加载过程线面会详细讲解。加载完数据以后,各个worker开始将自己load进来的点计算它属于的分区号,并查到属于哪个计算节点,并将其发送到相应计算节点。

        4.  最后,当数据都加载到各个worker上以后开始进行迭代计算。

五、          Giraph中涉及到多线程的地方

Giraph中,主要涉及到多线程的有两个地方,一个是worker在将split中所指的数据加载到内存时使用了线程;另一个是worker在迭代中对顶点计算时候使用了多线程。

先说第一个,当master把splits信息放到zookeeper上以后,每个Worker创建N个InputsCallable线程读取数据。N=Min(NUM_INPUT_THREADS,maxInputSplitThread),其中NUM_INPUT_THREADS默认值为1,maxInputSplitThread=InputSplitSize-1/maxWorkers+1。那么,默认每个worker就是创建一个线程来加载数据。在每个线程中,它们会不停的遍历splits列表上的信息,当遇到一个没有被加载的splits时,首先会在zookeeper上创建相应splits的正在加载标签,然后加载完以后会创建已加载标签。当加载完这个split之后继续进行上述过程,进行加载。

再说第二个,当worker把数据加载到内存并且已经将顶点发送到属于它的worker上以后,每个worker会得到若干个分区(原因在前面已经讲过)。此时开始进行迭代计算,也是pregel的核心内容。具体的,每个worker会开启N个线程来进行计算,此时worker会将本地的partionID存储到一个队列,partionID所对应的数据会放在一个叫做serviceWorker的对象中存储。每个线程中,它会不停地遍历属于本worker上的partion,对每一个partion上的顶点进行遍历运行用户定义的compute函数,直到所有partion完成。

需要说明的是,上述的“N”是可以自定义的,不过一般都是采用默认。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值