Spark Graphx Pregel
一.Pregel概述
1.什么是pregel?
Pregel是Google 提出的用于大规模分布式图计算框架。Pregel是个强大的基于图的迭代算法。
2.pregel应用场景
一般pregel可以在图中进行迭代计算,如求最短路径,关键路径,n度关系等。
二.Pregel源码及参数解释
1.源码
def pregel[A: ClassTag](
initialMsg: A,
maxIterations: Int = Int.MaxValue,
activeDirection: EdgeDirection = EdgeDirection.Either)(
vprog: (VertexId, VD, A) => VD,
sendMsg: EdgeTriplet[VD, ED] => Iterator[(VertexId, A)],
mergeMsg: (A, A) => A)
: Graph[VD, ED] = {
Pregel(graph, initialMsg, maxIterations, activeDirection)(vprog, sendMsg, mergeMsg)
}
2.参数详细解释
(1)initialMsg
初始化消息。这个初始化消息会被用来初始化图中的每个节点的属性,在pregel调用时,会首先在图上使用mapVertices来根据initialMsg的值更新每个几点的值。至于如何更新,则由vprog参数而定,vprog函数就接收了initialMsg消息作为参数来更新对应节点的值。
(2)maxIteration
最大迭代次数
(3)activeDirection
表示边的活跃方向。
-
活跃节点:是指在某一轮迭代中,pregel会以sendMsg和mergeMsg为参数来调用graph的aggregateMessage方法后收到消息的节点
-
活跃消息:是这轮迭代中所有被成功收到的消息
则有的边src节点是活跃节点,有的dst节点是活跃节点,有的边两端节点都是活跃节点。如果activeDirection参数被指定为"EdgeDirection.out",则在下一轮迭代中,只接收消息的出边(src—>dst)才会执行sendMsg函数。也就是说,sendMsg回调函数会过滤掉(dst—>src)的edgeTriplet上下文参数
(4)vprog
节点变换函数。
在初始时,以及每轮迭代后,pregel会根据上一轮使用的msg和这里的vprog函数在图上调用joinVertices方法变化每个收到消息的节点。
(5)sendMsg
消息发送函数。该函数的运行参数是一个代表边的上下文,pregel在调用aggregateMessage是,会将EdgeContext转换成EdgeTriplet对象来使用,用户需要通过Iterator[(VertexID,A)]指定发送哪些消息,发送哪些节点,发送哪些内容;因为在一条边上可以发送多个消息,如sendToDst,sendToSrc,所以这里是个Iterator,每个元素是一个tuple,其中的vertexId便是接收此消息的节点id,只能是该边上的srcId或者dstId,而A就是要发送的内容。
因此,如果要由src发送一条消息A到dst,则有:Iterator((dstId,A)),如果什么消息也不发送,则返回一个空的Iterator:Iterator.empty
(6)mergeMsg
邻居节点收到多条消息时的合并逻辑。
区别与vprog,mergeMsg仅能合并消息内容,但合并后并不会更新到节点中去,而vprog函数可以根据收到的消息(就是mergeMsg产生的结果)更新节点属性
三.Pregel计算顶点5 到 其他各顶点的 最短距离
1.图信息
(1)顶点信息
(1L, ("Alice", 28)),
(2L, ("Bob", 27)),
(3L, ("Charlie", 65)),
(4L, ("David", 42)),
(5L, ("Ed", 55)),
(6L, ("Fran", 50))
(2)边信息
Edge(2L, 1L, 7),
Edge(2L, 4L, 2),
Edge(3L, 2L, 4),
Edge(3L, 6L, 3),
Edge(4L, 1L, 1),
Edge(2L, 5L, 2),
Edge(5L, 3L, 8),
Edge(5L, 6L, 3)
2.Pregel原理分析
- 顶点的两个状态:
- 钝化态:类似于休眠,不做任何处理
- 激活态:可以进行数据的接受和发送
- 顶点能够处于激活状态需要的条件
- 成功收到消息
- 成功发送任何一条消息
(1)调用pregel方法之前
先把图的各个顶点的属性初始化,即顶点5到自己的距离为0,所以设为0,其他顶点都设为正无穷大Double.PositiveInifinity
(2)当调用pregel方法开始
首先,所有顶点都将接收到一条初始消息initialMsg ,使所有顶点都处于激活态(红色标识的节点)
(3)第一次迭代开始
所有顶点以EdgeDirection.Out的边方向调用sendMsg方法发送消息给目标顶点,如果 源顶点的属性+边的属性<目标顶点的属性,则发送消息。否则不发送。
5—>3(0+8<Double.Infinity,成功),
5—>6(0+3<Double.Infinity,成功),
3—>2(Double.Infinity+4>Double.Infinity,失败),
3—>6(Double.Infinity+3>Double.Infinity,失败),
2—>1(Double.Infinity+7>Double.Infinity,失败),
2—>4(Double.Infinity+2>Double.Infinity,失败),
2—>5(Double.Infinity+2>Double.Infinity,失败),
4—>1(Double.Infinity+1>Double.Infinity,失败)
sendMsg方法执行完成之后,根据顶点处于激活态的条件,顶点5成功地分别给顶点3和顶点6发送消息,顶点3 和 顶点6 也成功地接受到了消息。
所以此时只有5,3,6三个顶点处于激活状态,其他顶点全部钝化。然后收到消息的顶点3和顶点6都调用vprog方法,将收到的消息与自身属性合并。如图所示,至此第一次迭代结束。
(4)第二次迭代开始
顶点3 给 顶点6 发送消息失败,顶点3 给 顶点2 发送消息成功,此时 顶点3 成功发送消息,顶点2 成功接收消息,所以顶点2 和 顶点3 都成为激活状态,其他顶点都成为钝化状态。然后顶点2 调用vprog方法,将收到的消息 与 自身的属性合并。至此第二次迭代结束
3—>2(8+4<Double.Infinity,成功),
3—>6(8+3>3,失败)
(5)第三次迭代开始
顶点3分别发送消息给顶点2失败 和 顶点6失败,顶点2 分别发消息给 顶点1成功、顶点4成功、顶点5失败 ,所以 顶点2、顶点1、顶点4 成为激活状态,其他顶点为钝化状态。顶点1 和 顶点4分别调用vprog方法,将收到的消息 与 自身的属性合并。至此第三次迭代结束
3—>2(8+4=12,失败),
3—>6(8+3>3,失败)
2—>1(12+7<Double.Infinity,成功)
2—>4(12+2<Double.Infinity,成功)
(6)第四次迭代开始
顶点2 分别发送消息给 顶点1失败 和 顶点4失败。顶点4 给 顶点1发送消息成功,顶点1 和 顶点4 进入激活状态,其他顶点进入钝化状态。顶点1 调用vprog方法,将收到的消息 与 自身的属性合并
2—>1(12+7=19,失败)
2—>4(12+2=14,失败)
4—>1(14+1<19,成功)
(7)第五次迭代开始
顶点4 再给 顶点1发送消息失败,顶点4 和 顶点1 进入钝化状态,此时全图都进入钝化状态。至此结束
4—>1(14+1=15,失败)
结论:由上述分析过程可知,顶点5到其他各顶点距离全部算出,
5—>1 (15)
5—>2 (12)
5—>3 (8)
5—>4 (14)
5—>6 (3)
3.代码实现
package suanfa
import org.apache.spark.graphx._
import org.apache.spark