GraphX介绍

转自:http://www.javashuo.com/article/p-exdcvnhy-gw.htmlhtml

 

一、GraphX介绍

1.1 GraphX应用背景

Spark GraphX是一个分布式图处理框架,它是基于Spark平台提供对图计算和图挖掘简洁易用的而丰富的接口,极大的方便了对分布式图处理的需求。算法

众所周知·,社交网络中人与人之间有不少关系链,例如Twitter、Facebook、微博和微信等,这些都是大数据产生的地方都须要图计算,如今的图处理基本都是分布式的图处理,而并不是单机处理。Spark GraphX因为底层是基于Spark来处理的,因此自然就是一个分布式的图处理系统。apache

图的分布式或者并行处理实际上是把图拆分红不少的子图,而后分别对这些子图进行计算,计算的时候能够分别迭代进行分阶段的计算,即对图进行并行计算。下面咱们看一下图计算的简单示例:编程

clip_image002

从图中咱们能够看出:拿到Wikipedia的文档之后,能够变成Link Table形式的视图,而后基于Link Table形式的视图能够分析成Hyperlinks超连接,最后咱们可使用PageRank去分析得出Top Communities。在下面路径中的Editor Graph到Community,这个过程能够称之为Triangle Computation,这是计算三角形的一个算法,基于此会发现一个社区。从上面的分析中咱们能够发现图计算有不少的作法和算法,同时也发现图和表格能够作互相的转换。缓存

1.2  GraphX的框架

设计GraphX时,点分割和GAS都已成熟,在设计和编码中针对它们进行了优化,并在功能和性能之间寻找最佳的平衡点。如同Spark自己,每一个子模块都有一个核心抽象。GraphX的核心抽象是Resilient Distributed Property Graph,一种点和边都带属性的有向多重图。它扩展了Spark RDD的抽象,有Table和Graph两种视图,而只须要一份物理存储。两种视图都有本身独有的操做符,从而得到了灵活操做和执行效率。微信

clip_image004

如同Spark,GraphX的代码很是简洁。GraphX的核心代码只有3千多行,而在此之上实现的Pregel模式,只要短短的20多行。GraphX的代码结构总体下图所示,其中大部分的实现,都是围绕Partition的优化进行的。这在某种程度上说明了点分割的存储和相应的计算优化,的确是图计算框架的重点和难点。网络

1.3 发展历程

l早在0.5版本,Spark就带了一个小型的Bagel模块,提供了相似Pregel的功能。固然,这个版本还很是原始,性能和功能都比较弱,属于实验型产品。数据结构

l到0.8版本时,鉴于业界对分布式图计算的需求日益见涨,Spark开始独立一个分支Graphx-Branch,做为独立的图计算模块,借鉴GraphLab,开始设计开发GraphX。微信开发

l在0.9版本中,这个模块被正式集成到主干,虽然是Alpha版本,但已能够试用,小面包圈Bagel告别舞台。1.0版本,GraphX正式投入生产使用。框架

clip_image006

值得注意的是,GraphX目前依然处于快速发展中,从0.8的分支到0.9和1.0,每一个版本代码都有很多的改进和重构。根据观察,在没有改任何代码逻辑和运行环境,只是升级版本、切换接口和从新编译的状况下,每一个版本有10%~20%的性能提高。虽然和GraphLab的性能还有必定差距,但凭借Spark总体上的一体化流水线处理,社区热烈的活跃度及快速改进速度,GraphX具备强大的竞争力。

二、GraphX实现分析

如同Spark自己,每一个子模块都有一个核心抽象。GraphX的核心抽象是Resilient Distributed Property Graph,一种点和边都带属性的有向多重图。它扩展了Spark RDD的抽象,有Table和Graph两种视图,而只须要一份物理存储。两种视图都有本身独有的操做符,从而得到了灵活操做和执行效率。

clip_image008

GraphX的底层设计有如下几个关键点。

对Graph视图的全部操做,最终都会转换成其关联的Table视图的RDD操做来完成。这样对一个图的计算,最终在逻辑上,等价于一系列RDD的转换过程。所以,Graph最终具有了RDD的3个关键特性:Immutable、Distributed和Fault-Tolerant,其中最关键的是Immutable(不变性)。逻辑上,全部图的转换和操做都产生了一个新图;物理上,GraphX会有必定程度的不变顶点和边的复用优化,对用户透明。

 两种视图底层共用的物理数据,由RDD[Vertex-Partition]和RDD[EdgePartition]这两个RDD组成。点和边实际都不是以表Collection[tuple]的形式存储的,而是由VertexPartition/EdgePartition在内部存储一个带索引结构的分片数据块,以加速不一样视图下的遍历速度。不变的索引结构在RDD转换过程当中是共用的,下降了计算和存储开销。

clip_image010

图的分布式存储采用点分割模式,并且使用partitionBy方法,由用户指定不一样的划分策略(PartitionStrategy)。划分策略会将边分配到各个EdgePartition,顶点Master分配到各个VertexPartition,EdgePartition也会缓存本地边关联点的Ghost副本。划分策略的不一样会影响到所须要缓存的Ghost副本数量,以及每一个EdgePartition分配的边的均衡程度,须要根据图的结构特征选取最佳策略。目前有EdgePartition2d、EdgePartition1d、RandomVertexCut和CanonicalRandomVertexCut这四种策略。

2.1 存储模式

2.1.1 图存储模式

巨型图的存储整体上有边分割和点分割两种存储方式。2013年,GraphLab2.0将其存储方式由边分割变为点分割,在性能上取得重大提高,目前基本上被业界普遍接受并使用。

l边分割(Edge-Cut):每一个顶点都存储一次,但有的边会被打断分到两台机器上。这样作的好处是节省存储空间;坏处是对图进行基于边的计算时,对于一条两个顶点被分到不一样机器上的边来讲,要跨机器通讯传输数据,内网通讯流量大。

l点分割(Vertex-Cut):每条边只存储一次,都只会出如今一台机器上。邻居多的点会被复制到多台机器上,增长了存储开销,同时会引起数据同步问题。好处是能够大幅减小内网通讯量。

clip_image012

虽然两种方法互有利弊,但如今是点分割占上风,各类分布式图计算框架都将本身底层的存储形式变成了点分割。主要缘由有如下两个。

1.磁盘价格降低,存储空间再也不是问题,而内网的通讯资源没有突破性进展,集群计算时内网带宽是宝贵的,时间比磁盘更珍贵。这点就相似于常见的空间换时间的策略。

2.在当前的应用场景中,绝大多数网络都是“无尺度网络”,遵循幂律分布,不一样点的邻居数量相差很是悬殊。而边分割会使那些多邻居的点所相连的边大多数被分到不一样的机器上,这样的数据分布会使得内网带宽更加捉襟见肘,因而边分割存储方式被渐渐抛弃了。

2.1.2 GraphX存储模式

Graphx借鉴PowerGraph,使用的是Vertex-Cut(点分割)方式存储图,用三个RDD存储图数据信息:

lVertexTable(id, data):id为Vertex id,data为Edge data

lEdgeTable(pid, src, dst, data):pid为Partion id,src为原定点id,dst为目的顶点id

lRoutingTable(id, pid):id为Vertex id,pid为Partion id

点分割存储实现以下图所示:

clip_image014

2.2 计算模式

2.2.1 图计算模式

目前基于图的并行计算框架已经有不少,好比来自Google的Pregel、来自Apache开源的图计算框架Giraph/HAMA以及最为著名的GraphLab,其中Pregel、HAMA和Giraph都是很是相似的,都是基于BSP(Bulk Synchronous Parallell)模式。

Bulk Synchronous Parallell,即总体同步并行,它将计算分红一系列的超步(superstep)的迭代(iteration)。从纵向上看,它是一个串行模式,而从横向上看,它是一个并行的模式,每两个superstep之间设置一个栅栏(barrier),即总体同步点,肯定全部并行的计算都完成后再启动下一轮superstep。

clip_image015

每个超步(superstep)包含三部份内容:

1.计算compute:每个processor利用上一个superstep传过来的消息和本地的数据进行本地计算;

2.消息传递:每个processor计算完毕后,将消息传递个与之关联的其它processors

3.总体同步点:用于总体同步,肯定全部的计算和消息传递都进行完毕后,进入下一个superstep。

2.2.2GraphX计算模式

如同Spark同样,GraphX的Graph类提供了丰富的图运算符,大体结构以下图所示。能够在官方GraphX Programming Guide中找到每一个函数的详细说明,本文仅讲述几个须要注意的方法。

clip_image017

2.2.2.1 图的缓存

每一个图是由3个RDD组成,因此会占用更多的内存。相应图的cache、unpersist和checkpoint,更须要注意使用技巧。出于最大限度复用边的理念,GraphX的默认接口只提供了unpersistVertices方法。若是要释放边,调用g.edges.unpersist()方法才行,这给用户带来了必定的不便,但为GraphX的优化提供了便利和空间。参考GraphX的Pregel代码,对一个大图,目前最佳的实践是:

clip_image018

大致之意是根据GraphX中Graph的不变性,对g作操做并赋回给g以后,g已不是原来的g了,并且会在下一轮迭代使用,因此必须cache。另外,必须先用prevG保留住对原来图的引用,并在新图产生后,快速将旧图完全释放掉。不然,十几轮迭代后,会有内存泄漏问题,很快耗光做业缓存空间。

2.2.2.2 邻边聚合

mrTriplets(mapReduceTriplets)是GraphX中最核心的一个接口。Pregel也基于它而来,因此对它的优化能很大程度上影响整个GraphX的性能。mrTriplets运算符的简化定义是:

clip_image019

它的计算过程为:map,应用于每个Triplet上,生成一个或者多个消息,消息以Triplet关联的两个顶点中的任意一个或两个为目标顶点;reduce,应用于每个Vertex上,将发送给每个顶点的消息合并起来。

mrTriplets最后返回的是一个VertexRDD[A],包含每个顶点聚合以后的消息(类型为A),没有接收到消息的顶点不会包含在返回的VertexRDD中。

在最近的版本中,GraphX针对它进行了一些优化,对于Pregel以及全部上层算法工具包的性能都有重大影响。主要包括如下几点。

1. Caching for Iterative mrTriplets & Incremental Updates for Iterative mrTriplets:在不少图分析算法中,不一样点的收敛速度变化很大。在迭代后期,只有不多的点会有更新。所以,对于没有更新的点,下一次mrTriplets计算时EdgeRDD无需更新相应点值的本地缓存,大幅下降了通讯开销。

2.Indexing Active Edges:没有更新的顶点在下一轮迭代时不须要向邻居从新发送消息。所以,mrTriplets遍历边时,若是一条边的邻居点值在上一轮迭代时没有更新,则直接跳过,避免了大量无用的计算和通讯。

3.Join Elimination:Triplet是由一条边和其两个邻居点组成的三元组,操做Triplet的map函数经常只需访问其两个邻居点值中的一个。例如,在PageRank计算中,一个点值的更新只与其源顶点的值有关,而与其所指向的目的顶点的值无关。那么在mrTriplets计算中,就不须要VertexRDD和EdgeRDD的3-way join,而只须要2-way join。

全部这些优化使GraphX的性能逐渐逼近GraphLab。虽然还有必定差距,但一体化的流水线服务和丰富的编程接口,能够弥补性能的微小差距。

2.2.2.3 进化的Pregel模式

GraphX中的Pregel接口,并不严格遵循Pregel模式,它是一个参考GAS改进的Pregel模式。定义以下:

clip_image020

这种基于mrTrilets方法的Pregel模式,与标准Pregel的最大区别是,它的第2段参数体接收的是3个函数参数,而不接收messageList。它不会在单个顶点上进行消息遍历,而是将顶点的多个Ghost副本收到的消息聚合后,发送给Master副本,再使用vprog函数来更新点值。消息的接收和发送都被自动并行化处理,无需担忧超级节点的问题。

常见的代码模板以下所示:

clip_image021

能够看到,GraphX设计这个模式的用意。它综合了Pregel和GAS二者的优势,即接口相对简单,又保证性能,能够应对点分割的图存储模式,胜任符合幂律分布的天然图的大型计算。另外,值得注意的是,官方的Pregel版本是最简单的一个版本。对于复杂的业务场景,根据这个版本扩展一个定制的Pregel是很常见的作法。

2.2.2.4 图算法工具包

GraphX也提供了一套图算法工具包,方便用户对图进行分析。目前最新版本已支持PageRank、数三角形、最大连通图和最短路径等6种经典的图算法。这些算法的代码实现,目的和重点在于通用性。若是要得到最佳性能,能够参考其实现进行修改和扩展知足业务需求。另外,研读这些代码,也是理解GraphX编程最佳实践的好方法。

三、GraphX实例

3.1  图例演示

3.1.1 例子介绍

下图中有6我的,每一个人有名字和年龄,这些人根据社会关系造成8条边,每条边有其属性。在如下例子演示中将构建顶点、边和图,打印图的属性、转换操做、结构操做、链接操做、聚合操做,并结合实际要求进行演示。

clip_image023

3.1.2 程序代码

import org.apache.log4j.{Level, Logger}

import org.apache.spark.{SparkContext, SparkConf}

import org.apache.spark.graphx._

import org.apache.spark.rdd.RDD

 

object GraphXExample {

  def main(args: Array[String]) {

    //屏蔽日志

    Logger.getLogger("org.apache.spark").setLevel(Level.WARN)

    Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)

 

    //设置运行环境

    val conf = new SparkConf().setAppName("SimpleGraphX").setMaster("local")

    val sc = new SparkContext(conf)

 

    //设置顶点和边,注意顶点和边都是用元组定义的Array

    //顶点的数据类型是VD:(String,Int)

    val vertexArray = Array(

      (1L, ("Alice", 28)),

      (2L, ("Bob", 27)),

      (3L, ("Charlie", 65)),

      (4L, ("David", 42)),

      (5L, ("Ed", 55)),

      (6L, ("Fran", 50))

    )

    //边的数据类型ED:Int

    val edgeArray = Array(

      Edge(2L, 1L, 7),

      Edge(2L, 4L, 2),

      Edge(3L, 2L, 4),

      Edge(3L, 6L, 3),

      Edge(4L, 1L, 1),

      Edge(5L, 2L, 2),

      Edge(5L, 3L, 8),

      Edge(5L, 6L, 3)

    )

 

    //构造vertexRDD和edgeRDD

    val vertexRDD: RDD[(Long, (String, Int))] = sc.parallelize(vertexArray)

    val edgeRDD: RDD[Edge[Int]] = sc.parallelize(edgeArray)

 

    //构造图Graph[VD,ED]

    val graph: Graph[(String, Int), Int] = Graph(vertexRDD, edgeRDD)

 

    //***********************************************************************************

    //***************************  图的属性    ****************************************

    //**********************************************************************************        println("***********************************************")

    println("属性演示")

    println("**********************************************************")

    println("找出图中年龄大于30的顶点:")

    graph.vertices.filter { case (id, (name, age)) => age > 30}.collect.foreach {

      case (id, (name, age)) => println(s"nameisnameisage")

    }

 

    //边操做:找出图中属性大于5的边

    println("找出图中属性大于5的边:")

graph.edges.filter(e => e.attr > 5).collect.foreach(e => println(s"e.srcIdtoe.srcIdto{e.dstId} att ${e.attr}"))

    println

 

    //triplets操做,((srcId, srcAttr), (dstId, dstAttr), attr)

    println("列出边属性>5的tripltes:")

    for (triplet <- graph.triplets.filter(t => t.attr > 5).collect) {

      println(s"triplet.srcAttr.1likestriplet.srcAttr.1likes{triplet.dstAttr._1}")

    }

    println

 

    //Degrees操做

    println("找出图中最大的出度、入度、度数:")

    def max(a: (VertexId, Int), b: (VertexId, Int)): (VertexId, Int) = {

      if (a._2 > b._2) a else b

    }

    println("max of outDegrees:" + graph.outDegrees.reduce(max) + " max of inDegrees:" + graph.inDegrees.reduce(max) + " max of Degrees:" + graph.degrees.reduce(max))

    println

   

    //***********************************************************************************

    //***************************  转换操做    ****************************************

    //**********************************************************************************  

    println("**********************************************************")

    println("转换操做")

    println("**********************************************************")

    println("顶点的转换操做,顶点age + 10:")

    graph.mapVertices{ case (id, (name, age)) => (id, (name, age+10))}.vertices.collect.foreach(v => println(s"v.2.1isv.2.1is{v._2._2}"))

    println

    println("边的转换操做,边的属性*2:")

    graph.mapEdges(e=>e.attr*2).edges.collect.foreach(e => println(s"e.srcIdtoe.srcIdto{e.dstId} att ${e.attr}"))

    println

  

      //***********************************************************************************

    //***************************  结构操做    ****************************************

    //**********************************************************************************  

    println("**********************************************************")

    println("结构操做")

    println("**********************************************************")

    println("顶点年纪>30的子图:")

    val subGraph = graph.subgraph(vpred = (id, vd) => vd._2 >= 30)

    println("子图全部顶点:")

    subGraph.vertices.collect.foreach(v => println(s"v.2.1isv.2.1is{v._2._2}"))

    println

    println("子图全部边:")

    subGraph.edges.collect.foreach(e => println(s"e.srcIdtoe.srcIdto{e.dstId} att ${e.attr}"))

    println

 

   

      //***********************************************************************************

    //***************************  链接操做    ****************************************

    //**********************************************************************************  

    println("**********************************************************")

    println("链接操做")

    println("**********************************************************")

    val inDegrees: VertexRDD[Int] = graph.inDegrees

    case class User(name: String, age: Int, inDeg: Int, outDeg: Int)

 

    //建立一个新图,顶点VD的数据类型为User,并从graph作类型转换

    val initialUserGraph: Graph[User, Int] = graph.mapVertices { case (id, (name, age)) => User(name, age, 0, 0)}

 

    //initialUserGraph与inDegrees、outDegrees(RDD)进行链接,并修改initialUserGraph中inDeg值、outDeg值

    val userGraph = initialUserGraph.outerJoinVertices(initialUserGraph.inDegrees) {

      case (id, u, inDegOpt) => User(u.name, u.age, inDegOpt.getOrElse(0), u.outDeg)

    }.outerJoinVertices(initialUserGraph.outDegrees) {

      case (id, u, outDegOpt) => User(u.name, u.age, u.inDeg,outDegOpt.getOrElse(0))

    }

 

    println("链接图的属性:")

userGraph.vertices.collect.foreach(v => println(s"v.2.nameinDeg:v.2.nameinDeg:{v._2.inDeg} outDeg: ${v._2.outDeg}"))

    println

 

    println("出度和入读相同的人员:")

    userGraph.vertices.filter {

      case (id, u) => u.inDeg == u.outDeg

    }.collect.foreach {

      case (id, property) => println(property.name)

    }

    println

 

      //***********************************************************************************

    //***************************  聚合操做    ****************************************

    //**********************************************************************************  

    println("**********************************************************")

    println("聚合操做")

    println("**********************************************************")

    println("找出年纪最大的追求者:")

    val oldestFollower: VertexRDD[(String, Int)] = userGraph.mapReduceTriplets[(String, Int)](

      // 将源顶点的属性发送给目标顶点,map过程

      edge => Iterator((edge.dstId, (edge.srcAttr.name, edge.srcAttr.age))),

      // 获得最大追求者,reduce过程

      (a, b) => if (a._2 > b._2) a else b

    )

 

    userGraph.vertices.leftJoin(oldestFollower) { (id, user, optOldestFollower) =>

      optOldestFollower match {

        case None => s"${user.name} does not have any followers."

        case Some((name, age)) => s"nameistheoldestfollowerofnameistheoldestfollowerof{user.name}."

      }

    }.collect.foreach { case (id, str) => println(str)}

    println

 

     //***********************************************************************************

    //***************************  实用操做    ****************************************

    //**********************************************************************************

    println("**********************************************************")

    println("聚合操做")

    println("**********************************************************")

    println("找出5到各顶点的最短:")

    val sourceId: VertexId = 5L // 定义源点

    val initialGraph = graph.mapVertices((id, _) => if (id == sourceId) 0.0 else Double.PositiveInfinity)

    val sssp = initialGraph.pregel(Double.PositiveInfinity)(

      (id, dist, newDist) => math.min(dist, newDist),

      triplet => {  // 计算权重

        if (triplet.srcAttr + triplet.attr < triplet.dstAttr) {

          Iterator((triplet.dstId, triplet.srcAttr + triplet.attr))

        } else {

          Iterator.empty

        }

      },

      (a,b) => math.min(a,b) // 最短距离

    )

    println(sssp.vertices.collect.mkString("\n"))

 

    sc.stop()

  }

}

3.1.3 运行结果

在IDEA(如何使用IDEA参见第3课《3.Spark编程模型(下)--IDEA搭建及实战》)中首先对GraphXExample.Scala代码进行编译,编译经过后进行执行,执行结果以下:

**********************************************************

属性演示

**********************************************************

找出图中年龄大于30的顶点:

David is 42

Fran is 50

Charlie is 65

Ed is 55

找出图中属性大于5的边:

2 to 1 att 7

5 to 3 att 8

 

列出边属性>5的tripltes:

Bob likes Alice

Ed likes Charlie

 

找出图中最大的出度、入度、度数:

max of outDegrees:(5,3) max of inDegrees:(2,2) max of Degrees:(2,4)

 

**********************************************************

转换操做

**********************************************************

顶点的转换操做,顶点age + 10:

4 is (David,52)

1 is (Alice,38)

6 is (Fran,60)

3 is (Charlie,75)

5 is (Ed,65)

2 is (Bob,37)

 

边的转换操做,边的属性*2:

2 to 1 att 14

2 to 4 att 4

3 to 2 att 8

3 to 6 att 6

4 to 1 att 2

5 to 2 att 4

5 to 3 att 16

5 to 6 att 6

 

**********************************************************

结构操做

**********************************************************

顶点年纪>30的子图:

子图全部顶点:

David is 42

Fran is 50

Charlie is 65

Ed is 55

 

子图全部边:

3 to 6 att 3

5 to 3 att 8

5 to 6 att 3

 

**********************************************************

链接操做

**********************************************************

链接图的属性:

David inDeg: 1  outDeg: 1

Alice inDeg: 2  outDeg: 0

Fran inDeg: 2  outDeg: 0

Charlie inDeg: 1  outDeg: 2

Ed inDeg: 0  outDeg: 3

Bob inDeg: 2  outDeg: 2

 

出度和入读相同的人员:

David

Bob

 

**********************************************************

聚合操做

**********************************************************

找出年纪最大的追求者:

Bob is the oldest follower of David.

David is the oldest follower of Alice.

Charlie is the oldest follower of Fran.

Ed is the oldest follower of Charlie.

Ed does not have any followers.

Charlie is the oldest follower of Bob.

 

**********************************************************

实用操做

**********************************************************

找出5到各顶点的最短:

(4,4.0)

(1,5.0)

(6,3.0)

(3,8.0)

(5,0.0)

(2,2.0)

clip_image025

3.2 PageRank 演示

3.2.1 例子介绍

PageRank, 即网页排名,又称网页级别、Google 左侧排名或佩奇排名。它是Google 创始人拉里· 佩奇和谢尔盖· 布林于1997 年构建早期的搜索系统原型时提出的连接分析算法。目前不少重要的连接分析算法都是在PageRank 算法基础上衍生出来的。PageRank 是Google 用于用来标识网页的等级/ 重要性的一种方法,是Google 用来衡量一个网站的好坏的惟一标准。在揉合了诸如Title 标识和Keywords 标识等全部其它因素以后, Google 经过PageRank 来调整结果,使那些更具“等级/ 重要性”的网页在搜索结果中令网站排名得到提高,从而提升搜索结果的相关性和质量。

clip_image027

3.2.2 测试数据

在这里测试数据为顶点数据graphx-wiki-vertices.txt和边数据graphx-wiki-edges.txt,能够在本系列附带资源/data/class9/目录中找到这两个数据文件,其中格式为:

l  顶点为顶点编号和网页标题

clip_image029

l  边数据由两个顶点构成

clip_image031

3.2.3 程序代码

import org.apache.log4j.{Level, Logger}

import org.apache.spark.{SparkContext, SparkConf}

import org.apache.spark.graphx._

import org.apache.spark.rdd.RDD

 

object PageRank {

  def main(args: Array[String]) {

    //屏蔽日志

    Logger.getLogger("org.apache.spark").setLevel(Level.WARN)

    Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)

 

    //设置运行环境

    val conf = new SparkConf().setAppName("PageRank").setMaster("local")

    val sc = new SparkContext(conf)

 

    //读入数据文件

    val articles: RDD[String] = sc.textFile("/home/Hadoop/IdeaProjects/data/graphx/graphx-wiki-vertices.txt")

    val links: RDD[String] = sc.textFile("/home/hadoop/IdeaProjects/data/graphx/graphx-wiki-edges.txt")

 

    //装载顶点和边

    val vertices = articles.map { line =>

      val fields = line.split('\t')

      (fields(0).toLong, fields(1))

    }

 

    val edges = links.map { line =>

      val fields = line.split('\t')

      Edge(fields(0).toLong, fields(1).toLong, 0)

    }

 

    //cache操做

    //val graph = Graph(vertices, edges, "").persist(StorageLevel.MEMORY_ONLY_SER)

    val graph = Graph(vertices, edges, "").persist()

    //graph.unpersistVertices(false)

 

    //测试

    println("**********************************************************")

    println("获取5个triplet信息")

    println("**********************************************************")

    graph.triplets.take(5).foreach(println(_))

 

    //pageRank算法里面的时候使用了cache(),故前面persist的时候只能使用MEMORY_ONLY

    println("**********************************************************")

    println("PageRank计算,获取最有价值的数据")

    println("**********************************************************")

    val prGraph = graph.pageRank(0.001).cache()

 

    val titleAndPrGraph = graph.outerJoinVertices(prGraph.vertices) {

      (v, title, rank) => (rank.getOrElse(0.0), title)

    }

 

    titleAndPrGraph.vertices.top(10) {

      Ordering.by((entry: (VertexId, (Double, String))) => entry._2._1)

    }.foreach(t => println(t._2._2 + ": " + t._2._1))

 

    sc.stop()

  }

}

3.2.4 运行结果

在IDEA中首先对PageRank.scala代码进行编译,编译经过后进行执行,执行结果以下:

**********************************************************

获取5个triplet信息

**********************************************************

((146271392968588,Computer Consoles Inc.),(7097126743572404313,Berkeley Software Distribution),0)

((146271392968588,Computer Consoles Inc.),(8830299306937918434,University of California, Berkeley),0)

((625290464179456,List of Penguin Classics),(1735121673437871410,George Berkeley),0)

((1342848262636510,List of college swimming and diving teams),(8830299306937918434,University of California, Berkeley),0)

((1889887370673623,Anthony Pawson),(8830299306937918434,University of California, Berkeley),0)

 

**********************************************************

PageRank计算,获取最有价值的数据

**********************************************************

University of California, Berkeley: 1321.111754312097

Berkeley, California: 664.8841977233583

Uc berkeley: 162.50132743397873

Berkeley Software Distribution: 90.4786038848606

Lawrence Berkeley National Laboratory: 81.90404939641944

George Berkeley: 81.85226118457985

Busby Berkeley: 47.871998218019655

Berkeley Hills: 44.76406979519754

Xander Berkeley: 30.324075347288037

Berkeley County, South Carolina: 28.908336483710308

clip_image033

四、参考资料

(1)《GraphX:基于Spark的弹性分布式图计算系统》http://lidrema.blog.163.com/blog/static/20970214820147199643788/

(2)《快刀初试:Spark GraphX在淘宝的实践》 http://www.csdn.NET/article/2014-08-07/2821097