graphX+pregel入门初使用
最近在学习graphX这样的图计算工具,暂且记录下学习成果
用graphX首先是学会用pregel接口:
def pregel[A: ClassTag] ( initialMsg: A, //第一次迭代发送的消息 maxIterations: Int = Int.MaxValue, //迭代次数设置,默认为无限大或直到活跃点数量为0 activeDirection: EdgeDirection = EdgeDirection.Either //设置消息发送的方向(有Either,Both,Out,In四种方向) vprog: ( VertexId, VD, A) => VD, //点接收消息后进行的操作 sendMsg: EdgeTriplet[VD, ED] => Iterator[(VertexId, A)], //每次迭代每个Triplet会沿着上面设置的方向从起点发送消息(点ID,消息)到接收点 mergeMsg: (A, A) => A //当一个点接收到两条消息时会有的合并操作 ) : Graph[VD, ED] = { Pregel(graph, initialMsg, maxIterations, activeDireaction)(vprog, sendMsg, mergeMsg) } )
其中消息发送方向设置的四个可能值分别代表的意思有人喜欢用出边和入边等等形容,但我更喜欢用以下形式去理解:
Either: 当Triplet的起点和终点有一个是活跃时发送
Both:当Triplet的起点和终点都是活跃时发送
In:只有当Tripet的终点是活跃时发送
Out:只有当Tripet的起点是活跃时发送
以上四种发送方向其实都是满足条件时沿着Triplet的起点到终点方向发送,不能反过来
(不知道什么是triplet的就把他当成一条两点间的边吧)
Pregel算法的大体过程是怎样的?以最短路径计算为例
1、首先初始化所有点的值(代表这个点到源点的最短距离),一般设置为无限大,源点设置为0
2、接着源点被激活,发送消息(一般消息为自身的值)给相邻的点
3、相邻点接收到信息,将消息中的值与自身的值做比较,(一般是比较消息值与边的和 和 自身的值谁更小),决定是否更改自身的值,每个更改过值的点将被激活
4、被激活的向自身周围的点发送消息
5、重复3和4步,直到迭代到达最大次数或没有存在活跃的点为止
以下自己写的用Pregel计算最短路径代码:
import org.apache.log4j.{Level,Logger} import org.apache.spark._ import org.apache.spark.graphx._ import org.apache.spark.rdd.RDD object MakeGraph { 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("MakeGraph") val sc = new SparkContext(conf) //读取文件 val graph = GraphLoader.edgeListFile(sc,"file:///usr/local/spark/mycode/graphX/src/main/scala/undirected_Yago_ER_graph.data").cache() println("--------------------------------------------------------------------") println("点的数量:"+graph.vertices.count()) println("边得数量:"+graph.edges.count()) println("--------------------------------------------------------------------") //设置源点为0,并把除源点外的所有点的最短距离设为无限大 val sourceId:VertexId = 1 val initialGraph = graph.mapVertices((id, _) => if (id == sourceId) 0.0 else Double.PositiveInfinity) //用pregel计算最短路径 val sssp = Pregel(initialGraph, Double.PositiveInfinity, Int.MaxValue, EdgeDirection.Either)( (id, dist, newDist) => math.min(dist, newDist),//点接收信息后的操作 triplet => { if(triplet.srcAttr + triplet.attr < triplet.dstAttr) { Iterator((triplet.dstId, triplet.srcAttr + triplet.attr))//发送(目标点Id, 消息)键值对到目标点 } else { Iterator.empty } }, (a,b) => math.min(a,b)//聚合操作 ) sssp.triplets.take(5000).map(println) println("--------------------------------------------------------------------") sssp.vertices.map{case((id,dist)) => if(id < 100) println((id, dist))}.map(println) println("--------------------------------------------------------------------") sssp.vertices.map(v => v._1+" "+v._2).saveAsTextFile("file:///usr/local/spark/mycode/graphX/src/main/scala/data") } }
写好代码后,用到根目录(比如这里是/usr/local/spark/mycode/graphX)用sbt打包
命令:
/usr/local/sbt/sbt package
会在当前目录生成一个target文件夹,里面有打包好的jar文件,接着就可以用spark运行了
命令:
/usr/local/spark/bin/spark-submit --class "MakeGraph" --master spark://Master:7077 /usr/local/spark/mycode/graphX/target/scala-2.11/makegraph_2.11-1.0.jar