一、Pregel介紹
Pregel是一種基於BSP模型實現的並行圖處理系統。
BSP(Bulk Synchronous Parallel Computing Model,塊同步並行計算模型,又稱“大同步”模型)計算過程包括一系列全局超步(所謂的超步就是計算中的一次迭代),每個超步主要包括三個組件:
- 局部計算:每個參與的處理器都有自身的計算任務。
- 通訊:處理器羣相互交換數據。
- 柵欄同步(Barrier Synchronization):當一個處理器遇到“路障”(或柵欄),會等到其他所有處理器完成它們的計算步驟。
爲了解決大型圖的分佈式計算問題,Pregel搭建了一套可擴展的、有容錯機制的平臺,該平臺提供了一套非常靈活的API,可以描述各種各樣的圖計算。
Pregel作爲分佈式圖計算的計算框架,主要用於圖遍歷、最短路徑、PageRank計算等等。
- Pregel計算模型以有向圖作爲輸入
- 有向圖的每個頂點都有一個String類型的頂點ID
- 每個頂點都有一個可修改的用戶自定義值與之關聯
- 每條有向邊都和其源頂點關聯,並記錄了其目標頂點ID
- 邊上有一個可修改的用戶自定義值與之關聯
- 在每個超步S中,圖中的所有頂點都會並行執行相同的用戶自定義函數。
- 每個頂點可以接收前一個超步(S-1)中發送給它的消息,修改其自身及其出射邊的狀態,併發送消息給其他頂點,甚至是修改整個圖的拓撲結構。
- 在這種計算模式中,“邊。”並不是核心對象,在邊上面不會運行相應的計算,只有頂點纔會執行用戶自定義函數進行相應計算
採用消息傳遞模型主要基於以下兩個原因:
- 消息傳遞具有足夠的表達能力,沒有必要使用遠程讀取或共享內存的方式.
- 有助於提升系統整體性能.
二、Pregel的計算過程
Pregel的計算過程是由一系列被稱爲“超步”的迭代組成的。
在每個超步中,每個頂點上面都會並行執行用戶自定義的函數,該函數描述了一個頂點V在一個超步S中需要執行的操作。
該函數可以讀取前一個超步(S-1)中其他頂點發送給頂點V的消息,執行相應計算後,修改頂點V及其出射邊的狀態,然後沿着頂點V的出射邊發送消息給其他頂點,而且,一個消息可能經過多條邊的傳遞後被髮送到任意已知ID的目標頂點上去。
這些消息將會在下一個超步(S+1)中被目標頂點接收,然後像上述過程一樣開始下一個超步(S+1)的迭代過程。
在Pregel計算過程中,一個算法什麼時候可以結束,是由所有頂點的狀態決定的。
在第0個超步,所有頂點處於活躍狀態。
當一個頂點不需要繼續執行進一步的計算時,就會把自己的狀態設置爲“停機”,進入非活躍狀態。
當一個處於非活躍狀態的頂點收到來自其他頂點的消息時,Pregel計算框架必須根據條件判斷來決定是否將其顯式喚醒進入活躍狀態。
當圖中所有的頂點都已經標識其自身達到“非活躍(inactive)”狀態,並且沒有消息在傳送的時候,算法就可以停止運行。
以上是搬運工... 轉載:https://blog.csdn.net/qq_38265137/article/details/80547763
三、Spark Graphx Pregel 應用
package net.ben
import org.apache.spark.graphx.{Edge, EdgeDirection, Graph}
import org.apache.spark.sql.SparkSession
object PregelDemo {
/**
* 數字的頂點的編號,並非頂點的值
*
* 1L --\
* 2L --- 4L -- 6L
* 3L --/ /
* 5L -------/
*
* 編號爲4L的頂點,它的值是頂點1L,2L,3L的值的和
* 1L + 2L + 3L = 4L
*
* 編號爲6L的頂點,它的值是頂點4L,5L的值的和
* 4L + 5L = 6L
*/
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("Spark GraphX Pregel Demo").master("local").getOrCreate()
val sc = spark.sparkContext
val vertices = sc.parallelize(Array((6L, 0), (4L, 0), (1L, 1000), (2L, 550), (5L, 300), (3L, 300)))
// 有向圖,Edge(src, dst, attr),
val edges = sc.parallelize(Array(Edge(1L, 4L, 1), Edge(2L, 4L, 1), Edge(3L, 4L, 1), Edge(4L, 6L, 1), Edge(5L, 6L, 1)))
val graph = Graph(vertices, edges)
val result = graph.pregel(0, activeDirection = EdgeDirection.Out)(
(_, vd, msg) => msg + vd, // vertexProgram 用戶自定義函數,計算頂點值
t => Iterator((t.dstId, t.srcAttr)), // sendMsg 用戶自定義函數,發送消息給下一超步
(x, y) => x + y // mergeMsg 用戶自定義函數,合併上一超步的消息
)
result.vertices.collect().foreach(println)
spark.stop
}
}
輸出結果:
(4,1850)
(1,1000)
(6,2150)
(3,300)
(5,300)
(2,550)
參考文獻:
https://blog.csdn.net/qq_38265137/article/details/80547763
http://spark.apache.org/docs/latest/graphx-programming-guide.html#pregel-api