1.图聚合操作aggregateMessages:
1.1 聚合操作:
aggregateMessages:许多图分析任务中的一个关键步骤是聚集每个顶点的邻域信息,
在GraphX中最核心的聚合操作就是aggregateMessages.它主要功能是向邻边发消息,合并邻边收到的消息.
1.2.sendMsg和mergeMsg
sendMsg:
sendMsg 函数以EdgeContext作为输入参数 , 没有返回值
EdgeContext 提供了两个消息的发送函数
sendToSrc: 将Msg类型的消息发送给源顶点
sendToDst: 将Msg类型的消息发送给目标顶点
mergeMsg
每个顶点收到的所有消息都会被聚集起来传递给 mergeMsg 函数
mergeMsg函数定义了如何将顶点收到的所有消息转换成我们需要的结果
3. 图的聚合操作案例演示:
package aggregate
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.graphx.{Edge, EdgeContext, Graph}
object aggreTest2 {
def sendMsg(ec:EdgeContext[Int,String,Int]):Unit = {
ec.sendToDst( ec.srcAttr +1)
}
def mergeMsg(a: Int , b:Int) :Int = {
math.max(a,b)
}
def sumEdgeCount( g:Graph[Int,String]):Graph[Int,String] = {
val verts = g.aggregateMessages[Int]( sendMsg , mergeMsg)
//verts.collect.foreach(println(_))
val g2 = Graph(verts ,g.edges)
//(4,(1,0))
println( "*************************************")
//g2.vertices.join(g.vertices).map( x => x._2._1 - x._2._2).reduce(_+_).collect.foreach(println(_))
val check = g2.vertices.join(g.vertices).map( x => x._2._1 - x._2._2).reduce(_+_)
println(check)
println( "*************************************")
if(check > 0)
sumEdgeCount(g2)
else
g
}
def main(args: Array[String]): Unit = {
//设置运行环境
val conf = new SparkConf().setAppName("SimpleGraphX").setMaster("local")
val sc = new SparkContext(conf)
sc.setLogLevel("WARN")
// 构建图
val myVertices = sc.parallelize(Array((1L, "张三"), (2L, "李四"), (3L, "王五"), (4L, "钱六"),
(5L, "领导")))
val myEdges = sc.makeRDD(Array( Edge(1L,2L,"朋友"),
Edge(2L,3L,"朋友") , Edge(3L,4L,"朋友"),
Edge(4L,5L,"上下级"),Edge(3L,5L,"上下级")
))
val myGraph = Graph(myVertices,myEdges)
val initGraph = myGraph.mapVertices((_,_)=>0)
sumEdgeCount(initGraph).vertices.collect.foreach(println(_))
}
}
2.Pregel API:
2.1 Pregel 和 Pregel API:
Pregel :
在Hadoop兴起之后,google又发布了三篇研究论文,分别阐述了了Caffeine、Pregel、Dremel三种技术,这三种技术也被成为google的新“三驾马车”,其中的Pregel是google提出的用于大规模分布式图计算框架。主要用于图遍历(BFS)、 最短路径(SSSP )、 PageRank计算等等计算 。
Pregel API :
图本质上是递归的数据结构,因为顶点的属性依赖于它们的邻居的属性,而邻居的属性又依赖于它们的邻居的属性。因此,许多重要的图算法会迭代地重新计算每个顶点的属性,直到达到一个定点条件。提出了一系列图形并行抽象来表达这些迭代算法。GraphX 根据谷歌的思路实现了 自己的 Pregel API
.2 整体同步并行计算模型 BSP:
生成消息并传递给下一个超步。直到当前超步结束并将所有消息 传给下一个超步后, 下一个超步才会开始。 这就是同步屏障。
2.3 Pregel API完成一个超步的内部细节:
2.4 pregel函数:
2.5 pregel 的 activeDirection 参数:
对于 一条 边 的 两个 顶点 srcId 和 dstId:
EdgeDirection. Out—— 当 srcId 收到 来自 上一 轮 迭代 的 消息 时, 就会 调用 sendMsg, 这 意味着 把 这条 边 当作srcId 的“ 出 边”。
EdgeDirection. In—— 当 dstId 收到 来自 上一 轮 迭代 的 消息 时, 就会 调用 sendMsg, 这 意味着 把 这条 边 当作 dstId 的“ 入 边”。
EdgeDirection. Either—— 只要 srcId 或 dstId 收到 来自 上一 轮 迭代 的 消息 时, 就会 调用 sendMsg。
EdgeDirection. Both—— 只有 srcId 和 dstId 都 收到 来自 上一 轮 迭代 的 消息 时, 才会 调用 sendMsg。
2.6Pregel案例:
package pregel
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.graphx.{Edge, Graph, VertexId}
import org.apache.spark.graphx.util.GraphGenerators
object GraphXTest1 {
def main(args: Array[String]): Unit = {
//设置运行环境
val conf = new SparkConf().setAppName("Pregel API GraphX").setMaster("local")
val sc = new SparkContext(conf)
sc.setLogLevel("WARN")
// 构建图
val myVertices = sc.parallelize(Array((1L, 0), (2L, 0), (3L, 0), (4L, 0),
(5L, 0)))
val myEdges = sc.makeRDD(Array( Edge(1L,2L,2.5),
Edge(2L,3L,3.6) , Edge(3L,4L,4.5),
Edge(4L,5L,0.1),Edge(3L,5L,5.2)
))
val myGraph = Graph(myVertices,myEdges)
val sourceId: VertexId = 1L // The ultimate source
// Initialize the graph such that all vertices except the root have distance infinity.
val initialGraph = myGraph.mapVertices((id, _) =>
if (id == sourceId) 0.0 else Double.PositiveInfinity)
val sssp: Graph[Double, Double] = initialGraph.pregel(Double.PositiveInfinity)(
(id, dist, newDist) => math.min(dist, newDist), // Vertex Program
triplet => { // Send Message
if (triplet.srcAttr + triplet.attr < triplet.dstAttr) {
Iterator((triplet.dstId, triplet.srcAttr + triplet.attr))
} else {
Iterator.empty
}
},
(a, b) => math.min(a, b) // Merge Message
)
sssp.vertices.collect.foreach(println(_))
}
}