Spark入門詳解(四)-網頁排名算子開發實例

個人博客原文鏈接

思路分析:
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()
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章