二、Spark Streaming DStream操作

一、DStream轉換操作

1、DStream無狀態轉換操作

每次統計,和之前批次無關,不會進行累計

  1. map(func) :對源DStream的每個元素,採用func函數進行轉換,得到一個新的Dstream

  2. flatMap(func): 與map相似,但是每個輸入項可用被映射爲0個或者多個輸出項

  3. filter(func): 返回一個新的DStream,僅包含源DStream中滿足函數func的項

  4. repartition(numPartitions): 通過創建更多或者更少的分區改變DStream的並行程度

  5. reduce(func):利用函數func聚集源DStream中每個RDD的元素,返回一個包含單元素RDDs的新DStream

  6. count():統計源DStream中每個RDD的元素數量

  7. union(otherStream): 返回一個新的DStream,包含源DStream和其他DStream的元素

  8. countByValue():應用於元素類型爲(K,V)的DStream上,返回一個(K,V)鍵值對類型的新DStream,每個鍵的值是在原DStream的每個RDD中的出現次數

  9. reduceByKey(func, [numTasks]):當在一個由(K,V)鍵值對組成的DStream上執行該操作時,返回一個新的由(K,V)鍵值對組成的DStream,每一個key的值均由給定的recuce函數(func)聚集起來

  10. join(otherStream, [numTasks]):當應用於兩個DStream(一個包含(K,V)鍵值對,一個包含(K,W)鍵值對),返回一個包含(K, (V, W))鍵值對的新Dstream

  11. cogroup(otherStream, [numTasks]):當應用於兩個DStream(一個包含(K,V)鍵值對,一個包含(K,W)鍵值對),返回一個包含(K, Seq[V], Seq[W])的元組

  12. transform(func):通過對源DStream的每個RDD應用RDD-to-RDD函數,創建一個新的DStream。支持在新的DStream中做任何RDD操作

2、DStream有狀態轉換操作

A、滑動窗口轉換操作

  • 事先設定一個滑動窗口的長度(也就是窗口的持續時間)

  • 設定滑動窗口的時間間隔(每隔多長時間執行一次計算),讓窗口按照指定時間間隔在源DStream上滑動

  • 每次窗口停放的位置上,都會有一部分Dstream(或者一部分RDD)被框入窗口內,形成一個小段的Dstream

  • 可以啓動對這個小段DStream的計算

(1)一些窗口轉換操作的含義

  1. window(windowLength, slideInterval) 基於源DStream產生的窗口化的批數據,計算得到一個新的Dstream

  2. countByWindow(windowLength, slideInterval) 返回流中元素的一個滑動窗口數

  3. reduceByWindow(func, windowLength, slideInterval) 返回一個單元素流。利用函數func聚集滑動時間間隔的流的元素創建這個單元素流。函數func必須滿足結合律,從而可以支持並行計算

  4. reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks]) 應用到一個(K,V)鍵值對組成的DStream上時,會返回一個由(K,V)鍵值對組成的新的DStream。每一個key的值均由給定的reduce函數(func函數)進行聚合計算。注意:在默認情況下,這個算子利用了Spark默認的併發任務數去分組。可以通過numTasks參數的設置來指定不同的任務數

  5. reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks]) 更加高效的reduceByKeyAndWindow,每個窗口的reduce值,是基於先前窗口的reduce值進行增量計算得到的;它會對進入滑動窗口的新數據進行reduce操作,並對離開窗口的老數據進行“逆向reduce”操作。但是,只能用於“可逆reduce函數”,即那些reduce函數都有一個對應的“逆向reduce函數”(以InvFunc參數傳入)

  6. countByValueAndWindow(windowLength, slideInterval, [numTasks]) 當應用到一個(K,V)鍵值對組成的DStream上,返回一個由(K,V)鍵值對組成的新的DStream。每個key的值都是它們在滑動窗口中出現的頻率

 

B、updateStateByKey操作

需要在跨批次之間維護狀態時,就必須使用updateStateByKey操作

詞頻統計實例:

對於有狀態轉換操作而言,本批次的詞頻統計,會在之前批次的詞頻統計結果的基礎上進行不斷累加,所以,最終統計得到的詞頻,是所有批次的單詞的總的詞頻統計結果

import org.apache.spark._
import org.apache.spark.streaming._
import org.apache.spark.storage.StorageLevel
object NetworkWordCountStateful {
  def main(args: Array[String]) {
    //定義狀態更新函數
    val updateFunc = (values: Seq[Int], state: Option[Int]) => {
      val currentCount = values.foldLeft(0)(_ + _)
      val previousCount = state.getOrElse(0)
      Some(currentCount + previousCount)
    }
    StreamingExamples.setStreamingLogLevels()  //設置log4j日誌級別    
    val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCountStateful")
    val sc = new StreamingContext(conf, Seconds(5))
 //設置檢查點,檢查點具有容錯機制
    sc.checkpoint("file:///usr/local/spark/mycode/streaming/stateful/")   
    val lines = sc.socketTextStream("localhost", 9999)
    val words = lines.flatMap(_.split(" "))
    val wordDstream = words.map(x => (x, 1))
    val stateDstream = wordDstream.updateStateByKey[Int](updateFunc)
    stateDstream.print()
    sc.start()
    sc.awaitTermination()
  }
}

二、輸出操作

在Spark應用中,外部系統經常需要使用到Spark DStream處理後的數據,因此,需要採用輸出操作把DStream的數據輸出到數據庫或者文件系統中

1 、把DStream輸出到文本文件中

package org.apache.spark.examples.streaming
import org.apache.spark._
import org.apache.spark.streaming._
import org.apache.spark.storage.StorageLevel
object NetworkWordCountStateful {
  def main(args: Array[String]) {
    //定義狀態更新函數
    val updateFunc = (values: Seq[Int], state: Option[Int]) => {
      val currentCount = values.foldLeft(0)(_ + _)
      val previousCount = state.getOrElse(0)
      Some(currentCount + previousCount)
    }
    StreamingExamples.setStreamingLogLevels()  //設置log4j日誌級別    
    val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCountStateful")
    val sc = new StreamingContext(conf, Seconds(5))
//設置檢查點,檢查點具有容錯機制
    sc.checkpoint("file:///usr/local/spark/mycode/streaming/dstreamoutput/")    
    val lines = sc.socketTextStream("localhost", 9999)
    val words = lines.flatMap(_.split(" "))
    val wordDstream = words.map(x => (x, 1))
    val stateDstream = wordDstream.updateStateByKey[Int](updateFunc)
    stateDstream.print()
    //下面是新增的語句,把DStream保存到文本文件中
    stateDstream.saveAsTextFiles("file:///usr/local/spark/mycode/streaming/dstreamoutput/output.txt")
    sc.start()
    sc.awaitTermination()
  }
}


2、把DStream寫入到MySQL數據庫中

package org.apache.spark.examples.streaming
import java.sql.{PreparedStatement, Connection, DriverManager}
import java.util.concurrent.atomic.AtomicInteger
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.StreamingContext._
import org.apache.spark.storage.StorageLevel
object NetworkWordCountStateful {
  def main(args: Array[String]) {
    //定義狀態更新函數
    val updateFunc = (values: Seq[Int], state: Option[Int]) => {
      val currentCount = values.foldLeft(0)(_ + _)
      val previousCount = state.getOrElse(0)
      Some(currentCount + previousCount)
    }
    StreamingExamples.setStreamingLogLevels()  //設置log4j日誌級別

    val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCountStateful")
    val sc = new StreamingContext(conf, Seconds(5))
//設置檢查點,檢查點具有容錯機制
    sc.checkpoint("file:///usr/local/spark/mycode/streaming/dstreamoutput/")    
    val lines = sc.socketTextStream("localhost", 9999)
    val words = lines.flatMap(_.split(" "))
    val wordDstream = words.map(x => (x, 1))
    val stateDstream = wordDstream.updateStateByKey[Int](updateFunc)
    stateDstream.print()
    //下面是新增的語句,把DStream保存到MySQL數據庫中
    stateDstream.foreachRDD(rdd => {
      //內部函數
      def func(records: Iterator[(String,Int)]) {
        var conn: Connection = null
        var stmt: PreparedStatement = null
        try {
          val url = "jdbc:mysql://localhost:3306/spark"
          val user = "root"
          val password = "hadoop"  //數據庫密碼是hadoop
          conn = DriverManager.getConnection(url, user, password)
          records.foreach(p => {
            val sql = "insert into wordcount(word,count) values (?,?)"
            stmt = conn.prepareStatement(sql);
            stmt.setString(1, p._1.trim)
            stmt.setInt(2,p._2.toInt)
            stmt.executeUpdate()
          })
        } catch {
          case e: Exception => e.printStackTrace()
        } finally {
          if (stmt != null) {
            stmt.close()
          }
          if (conn != null) {
            conn.close()
          }
        }
      }
      val repartitionedRDD = rdd.repartition(3)
      repartitionedRDD.foreachPartition(func)
    })
    sc.start()
    sc.awaitTermination()
  }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章