思路分析:
1.此操作需要用到兩個pairRDD,第一個pairRDD,key:當前頁面,value:當前頁面所含鏈接頁面的合集。
2.第二個pairRDD,key:當前頁面,value:當前頁面的權重
3.兩個pairRDD在循環中根據key進行連接操作,計算每次循環返回後各個頁面的權重值
4.根據權重值的大小可知頁面的排名
開發代碼:
object PageRankTest {
def main(args: Array[String]): Unit = {
// 設置日誌級別
Logger.getLogger("org").setLevel(Level.WARN)
// 初始化sc對象
val conf = new SparkConf().setMaster("local[2]").setAppName("My Spark")
val sc = SparkContext.getOrCreate(conf)
// 獲取程序開始時的時間
val start = System.currentTimeMillis()
// 建立模擬數據來測試
// key:當前頁面
// value:鏈接的頁面
val links = List(
("A",List("B","C")),
("B",List("C")),
("C",List("D")),
("D",List("A","C"))
)
// key:頁面
// value:權重 初始默認值爲1
val ranks = List(
("A",1.0),
("B",1.0),
("C",1.0),
("D",1.0)
)
// 創建頁面的RDD 分區 持久化
val linksRDD = sc.parallelize(links).partitionBy(new HashPartitioner(4)).persist()
// 創建權重的RDD
var ranksRDD = sc.parallelize(ranks)
/**
* 迭代多次獲取一個相對穩定的權重值
* 如果迭代次數過大,棧內存會溢出可以設置一下棧的最小內存
*
* 設置JVM虛擬內存的允許最小 最大 以及棧的最小內存
* -Xms1024m -Xmx2048m -Xss1024m
* */
// 設置迭代次數
val iterator = 50
(1 to iterator).foreach(x => {
/**
* 兩個RDD在循環中進行連接操作,需要對其中一個RDD提前預定義分區。
* 由於ranksRDD中的數據需要發生變化,因此對linksRDD進行預定義分區
*
* 3134
* 並行度=4 2674 並行度=5 2568
* */
// 連接操作 join key --> String
// RDD[(頁面,(鏈接數據集合,權重值))]
val joinRDD: RDD[(String, (List[String], Double))] = linksRDD.join(ranksRDD)
val newRDD: RDD[(String, Double)] = joinRDD.flatMap(x=>{
// 當前頁面
val page = x._1
// (鏈接數據集合,權重值)
val (linkSet,rank) = x._2
// 當前page分給其他鏈接頁面的平均值
val avg = rank / linkSet.size
// 鏈接的頁面,分給的均值
linkSet.map(page1 => {
(page1,avg)
})
})
// 按照key,即頁面相同,權重值聚合+ 聚合之後按照0.15+0.85*權重值重新修改值
ranksRDD = newRDD.reduceByKey(_+_).mapValues(rank => rank*0.85 + 0.15)
})
ranksRDD.foreach(println)
// 獲取程序結束時的時間
val end = System.currentTimeMillis()
println("耗時:" + (end - start))
sc.stop()
}
}