Spark GraphX之Pregel

参考地址:https://blog.csdn.net/hanweileilei/article/details/89764466

Pregel介绍

  • Pregel是google提出的用于大规模分布式图计算框架。主要用于图遍历(BFS)、最短路径(SSSP)、PageRank等计算

Pregel源码

  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)
  

Pregel参数分析

参数说明
initialMsg图初始化的时候,开始模型计算的时候,所有节点都会先收到一个消息
maxIterations最大迭代次数
activeDirection规定了发送消息的方向
vprog节点调用该消息将聚合后的数据和本节点进行属性的合并
sendMsg激活态的节点调用该方法发送消息
mergeMsg如果一个节点接收到多条消息,先用mergeMsg 来将多条消息聚合成为一条消息,如果节点只收到一条消息,则不调用该函数

Pregel示例:最短距离

具体操作前需要知道两个知识点

  1. 顶点存在两种状态:钝化态【类似于休眠,不做任何事】、激活态【干活】
  2. 顶点能够处于激活态需要有条件:成功收到消息 或者 成功发送了任何一条消息

测试数据

//Vertices测试数据
(1L, ("Alice", 28)),
(2L, ("Bob", 27)),
(3L, ("Charlie", 65)),
(4L, ("David", 42)),
(5L, ("Ed", 55)),
(6L, ("Fran", 50))

//Edge测试数据
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)

实现代码

package Graph

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.graphx._
import org.apache.spark.rdd.RDD

object pregel {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("GraphxHelloWorld").setMaster("local[*]")
    val sc = new SparkContext(conf)

    val vertexArray = Array(
      (1L, ("Alice", 28)),
      (2L, ("Bob", 27)),
      (3L, ("Charlie", 65)),
      (4L, ("David", 42)),
      (5L, ("Ed", 55)),
      (6L, ("Fran", 50))
    )
    //创建VerticesRDD
    val vertexRDD: RDD[(VertexId, (String,Int))] = sc.makeRDD(vertexArray)

    val edgeArray = Array(
      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)
    )
    //创建EdgeRDD
    val edgeRDD: RDD[Edge[Int]] = sc.makeRDD(edgeArray)

    //创建Graph
    val graph=Graph(vertexRDD,edgeRDD)

    //设置起始顶点ID
    val srcVertexID=5L

    //初始化顶点属性,非srcVertexID设定的起始顶点,属性都将设置为无穷大(Double.PositiveInfinity),起始顶点属性为0.0
    val initialGrraph: Graph[Double, PartitionID] = graph.mapVertices{case(id,(name,age))=>{if(id==srcVertexID) 0.0 else Double.PositiveInfinity}}

    //调用Pregel函数
    val pregelGraph: Graph[Double, PartitionID] = initialGrraph.pregel(
      //initialMsg:初始值
      //在开始时,sendMsg会向所有顶点发送消息,vprog聚合数据,使所有节点都处于激活态
      Double.PositiveInfinity,
      //maxIterations:迭代次数
      Int.MaxValue,
      //activeDirection:规定发送消息方向
      EdgeDirection.Out
    )(
      //vprog
      //节点将接受到的消息与自己的属性进行比较,若小于自身的属性,则将较小的值作为自身的新属性
      (vid: VertexId, vd: Double, disMsg: Double) => {
        val minDist = math.min(vd, disMsg)
        println("vprog:顶点 " + vid + " 属性 " +vd+ " 接收到的值 " + disMsg + " 较小的值为: " + minDist)
        minDist
      },
      //sendMsg
      //激活态的节点通过这个方法像其他节点发送消息
      (edgeTriplet: EdgeTriplet[Double, PartitionID]) => {
        if (edgeTriplet.srcAttr + edgeTriplet.attr < edgeTriplet.dstAttr) {
          println("顶点" + edgeTriplet.srcId + "给顶点" + edgeTriplet.dstId + "发送消息")
          Iterator[(VertexId, Double)]((edgeTriplet.dstId, edgeTriplet.srcAttr + edgeTriplet.attr))
        } else {
          Iterator.empty
        }
      },
      //一节点节点接收到多条消息,会先通过这个方法聚合为一条消息,如果节点只接收到一条消息,则不会掉用这个方法
      (msg1: Double, msg2: Double) => math.min(msg1, msg2)
    )
    //显示5号顶点距离其他顶点的最短距离
    pregelGraph.vertices.foreach(println)
  }
}

结果输出

//初始化
vprog:顶点 3 属性 Infinity 接收到的值 Infinity 较小的值为: Infinity
vprog:顶点 2 属性 Infinity 接收到的值 Infinity 较小的值为: Infinity
vprog:顶点 4 属性 Infinity 接收到的值 Infinity 较小的值为: Infinity
vprog:顶点 5 属性 0.0 接收到的值 Infinity 较小的值为: 0.0
vprog:顶点 1 属性 Infinity 接收到的值 Infinity 较小的值为: Infinity
vprog:顶点 6 属性 Infinity 接收到的值 Infinity 较小的值为: Infinity
//第一次迭代
顶点5给顶点6发送消息
顶点5给顶点3发送消息
vprog:顶点 3 属性 Infinity 接收到的值 8.0 较小的值为: 8.0
vprog:顶点 6 属性 Infinity 接收到的值 3.0 较小的值为: 3.0
//第二次迭代
顶点3给顶点2发送消息
vprog:顶点 2 属性 Infinity 接收到的值 12.0 较小的值为: 12.0
//第三次迭代
顶点2给顶点1发送消息
顶点2给顶点4发送消息
vprog:顶点 4 属性 Infinity 接收到的值 14.0 较小的值为: 14.0
vprog:顶点 1 属性 Infinity 接收到的值 19.0 较小的值为: 19.0
//第四次迭代
顶点4给顶点1发送消息
vprog:顶点 1 属性 19.0 接收到的值 15.0 较小的值为: 15.0
//5号顶点距离其他顶点的最短路径
(2,12.0)
(3,8.0)
(6,3.0)
(5,0.0)
(1,15.0)
(4,14.0)

Pregel:最短路径原理分析

pregel开始初始化:

首先,所有顶点都将接收到一条初始消息initialMsg ,使所有顶点都处于激活态(红色标识的节点)。

在这里插入图片描述
第一次迭代开始:

所有顶点以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方法,将收到的消息 与 自身的属性合并。如下图2所示。到此第一次迭代结束。

在这里插入图片描述
第二次迭代开始:

顶点3 给 顶点6 发送消息失败,顶点3 给 顶点2 发送消息成功,此时 顶点3 成功发送消息,顶点2 成功接收消息,所以顶点2 和 顶点3 都成为激活状态,其他顶点都成为钝化状态。然后顶点2 调用vprog方法,将收到的消息 与 自身的属性合并。 见图3. 至此第二次迭代结束。

在这里插入图片描述
第三次迭代开始:

顶点3分别发送消息给顶点2失败 和 顶点6失败,顶点2 分别发消息给 顶点1成功、顶点4成功、顶点5失败 ,所以 顶点2、顶点1、顶点4 成为激活状态,其他顶点为钝化状态。顶点1 和 顶点4分别调用vprog方法,将收到的消息 与 自身的属性合并。见图4。至此第三次迭代结束

在这里插入图片描述
第四次迭代开始:

顶点2 分别发送消息给 顶点1失败 和 顶点4失败。顶点4 给 顶点1发送消息成功,顶点1 和 顶点4 进入激活状态,其他顶点进入钝化状态。顶点1 调用vprog方法,将收到的消息 与 自身的属性合并 。见图5

在这里插入图片描述

第五次迭代开始:

顶点4 再给 顶点1发送消息失败,顶点4 和 顶点1 进入钝化状态,此时全图都进入钝化状态。至此结束,见图6.

在这里插入图片描述

到这里,Pregel最短距离示例就结束了,文字相较多一些,但不难理解,不太明白的建议多看两遍原理分析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值