Spark GraphX 中的 pregel 算法

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

案例: 求顶点5 到其他各顶点的最短距离


在理解案例之前,首先要清楚关于 顶点 的两点知识:

  1. 顶点 的状态有两种:
    (1)、钝化态【类似于休眠,不做任何事】
    (2)、激活态【干活】
  2. 顶点 能够处于激活态需要有条件:
    (1)、成功收到消息 或者
    (2)、成功发送了任何一条消息
	//1、创建SparkContext
    val spark: SparkConf = new SparkConf().setAppName("PregelDemo")
      .setMaster("local[*]")
    val sc = new SparkContext(spark)
    //2、创建顶点
    val vertexArray = Array(
      (1L, ("Alice", 28)),
      (2L, ("Bob", 27)),
      (3L, ("Charlie", 65)),
      (4L, ("David", 42)),
      (5L, ("Ed", 55)),
      (6L, ("Fran", 50))
    )
    val vertexRDD: RDD[(VertexId, (String,Int))] = sc.makeRDD(vertexArray)
    //3、创建边,边的属性代表 相邻两个顶点之间的距离
    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)
    )
    val edgeRDD: RDD[Edge[Int]] = sc.makeRDD(edgeArray)
    //4、创建图(使用aply方式创建)
    val graph1 = Graph(vertexRDD, edgeRDD)

    /* ************************** 使用pregle算法计算,顶点5 到 各个顶点的最短距离 ************************** */

    //被计算的图中 起始顶点id
    val srcVertexId = 5L
    val initialGraph: Graph[Double, PartitionID] = graph1.mapVertices{
    
    case (vid,(name,age)) => if(vid==srcVertexId) 0.0 else Double.PositiveInfinity}
//    initialGraph.vertices.collect().foreach(println)

    //5、调用pregel
    val pregelGraph: Graph[Double, PartitionID] = initialGraph.pregel(
      Double.PositiveInfinity,
      Int.MaxValue,
      EdgeDirection.Out
    )(
      (vid: VertexId, vd: Double, distMsg: Double) => {
    
    
        val minDist: Double = math.min(vd, distMsg)
        println("vprog: " + vid + " " + vd + " " + distMsg + " " + minDist)
        minDist
      },
      (edgeTriplet: EdgeTriplet[Double,PartitionID]) => {
    
    
        if (edgeTriplet.srcAttr + edgeTriplet.attr < edgeTriplet.dstAttr) {
    
    
          println("sendMsg " + edgeTriplet.srcId + " " + edgeTriplet.srcAttr+" "+
            edgeTriplet.dstId + " " + edgeTriplet.dstAttr+" "+edgeTriplet.attr)
          Iterator[(VertexId,Double)]((edgeTriplet.dstId,edgeTriplet.srcAttr+edgeTriplet.attr))
        } else {
    
    
          Iterator.empty
        }
      },
      (msg1: Double, msg2: Double) => {
    
    
        println("msg1: "+msg1+"msg2: "+msg2)
        math.min(msg1, msg2)
      }
    )
    println("输出结果:")
    pregelGraph.triplets.collect().foreach(println)
    println()
    pregelGraph.vertices.collect().foreach(println)

结果如下(省略其他中间过程的打印输出):

(1,15.0)
(2,12.0)
(3,8.0)
(4,14.0)
(5,0.0)
(6,3.0)

pregel 原理分析


调用pregel方法之前,先把图的各个顶点的属性初始化为如下图所示:顶点5到自己的距离为0,所以设为0,其他顶点都设为正无穷大Double.PositiveInfinity

当调用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 方法,将收到的消息 与 自身的属性合并
在这里插入图片描述

第五次迭代开始:

顶点4 再给 顶点1发送消息失败,顶点4 和 顶点1 进入钝化状态,此时全图都进入钝化状态。至此结束
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42578036/article/details/110194141
今日推荐